目录
[二、核心特性 ACID](#二、核心特性 ACID)
[1. 原子性(Atomicity):要么全成,要么全败](#1. 原子性(Atomicity):要么全成,要么全败)
[2. 一致性(Consistency):数据始终合法](#2. 一致性(Consistency):数据始终合法)
[3. 隔离性(Isolation):并发执行不干扰](#3. 隔离性(Isolation):并发执行不干扰)
[4. 持久性(Durability):提交后永久生效](#4. 持久性(Durability):提交后永久生效)
[3. 自动提交 vs 手动提交](#3. 自动提交 vs 手动提交)
[四、隔离级别:解决并发事务的 "冲突"](#四、隔离级别:解决并发事务的 “冲突”)
[1. 4 种隔离级别(从低到高)](#1. 4 种隔离级别(从低到高))
[2. 隔离级别相关问题](#2. 隔离级别相关问题)
[3. 隔离级别的查看与设置](#3. 隔离级别的查看与设置)
[五、隔离性的实现:锁与 MVCC](#五、隔离性的实现:锁与 MVCC)
[1. 锁机制:控制并发修改](#1. 锁机制:控制并发修改)
[2. MVCC:多版本并发控制(非锁定读)](#2. MVCC:多版本并发控制(非锁定读))
[1. 死锁产生的 4 个条件](#1. 死锁产生的 4 个条件)
[2. 避免死锁的方法](#2. 避免死锁的方法)
一、事务定义
事务是将一组 SQL 操作打包成的 "不可分割的执行单元",要么全部成功(提交) ,要么全部失败(回滚) ,绝不存在 "执行一半" 的中间状态。
最经典的例子就是转账:张三给李四转 100 元,核心是两条 SQL------ 张三余额 - 100、李四余额 + 100。事务能保证:要么两条 SQL 都执行成功,要么都不执行,不会出现 "张三钱少了但李四没收到" 的情况。
二、核心特性 ACID
ACID 是事务的四大核心特性,具体介绍如下。
1. 原子性(Atomicity):要么全成,要么全败
定义:事务中的所有操作是一个整体,不可拆分,失败则回滚到执行前状态。
实现原理:依赖 UndoLog(回滚日志) 。执行 SQL 前,会记录数据的原始状态到 UndoLog;若事务失败,通过 UndoLog 反向执行,恢复数据。
2. 一致性(Consistency):数据始终合法
定义:事务执行前后,数据库的完整性约束(如总额不变、字段格式合法)不会被破坏。
实现原理:一致性是 "结果",依赖原子性、隔离性、持久性共同保障。比如转账前后总额不变,就是一致性的体现。
3. 隔离性(Isolation):并发执行不干扰
定义:多个事务同时执行时,彼此不会相互影响,每个事务都能看到独立的数据视图。
实现原理:通过 锁机制 和 MVCC(多版本并发控制) 实现,具体依赖隔离级别配置。
4. 持久性(Durability):提交后永久生效
定义:事务提交后,对数据的修改会永久存储,即使系统崩溃(断电、宕机)也不会丢失。
实现原理:依赖 RedoLog(重做日志) 。事务执行时,先将修改记录到 RedoLog;提交时,RedoLog 刷入磁盘;系统崩溃后,通过 RedoLog 恢复已提交的修改。
三、事务的使用方法
MySQL 中仅 InnoDB 存储引擎支持事务,核心语法和实操场景如下:
1、核心语法
sql
-- 开启事务(二选一)
START TRANSACTION; 或 BEGIN;
-- 执行SQL操作(增删改)
UPDATE bank_account SET balance = balance - 100 WHERE name = '张三';
UPDATE bank_account SET balance = balance + 100 WHERE name = '李四';
-- 提交事务(成功则持久化)
COMMIT;
-- 回滚事务(失败则恢复)
ROLLBACK;
2、实用功能:保存点(Savepoint)
在事务执行的过程中设置保存点,回滚时指定保存点可以把数据恢复到保存点的状态:
sql
START TRANSACTION;
UPDATE bank_account SET balance = balance - 100 WHERE name = '张三'; -- 张三余额900
SAVEPOINT sp1; -- 设置保存点
UPDATE bank_account SET balance = balance + 100 WHERE name = '李四'; -- 李四余额1100
SAVEPOINT sp2; -- 第二个保存点
UPDATE bank_account SET balance = balance - 100 WHERE name = '张三'; -- 张三余额800
ROLLBACK TO sp1; -- 回滚到保存点sp1,此时张三900、李四1000
COMMIT;
3. 自动提交 vs 手动提交
- MySQL 默认 自动提交(autocommit=ON) :每条 SQL 单独构成一个事务,执行后自动提交。
- 手动提交:设置**
SET AUTOCOMMIT=0**(或 OFF),需手动执行 COMMIT/ROLLBACK。- 注意:只要用**
START TRANSACTION**开启事务,无论 autocommit 是否开启,都必须手动 COMMIT 才会持久化;手动提交模式下,不用显示开启事务,执行修改操作后,提交或回滚事务时直接使用 commit 或 rollback;已提交的事务不能回滚。
四、隔离级别:解决并发事务的 "冲突"
当多个事务同时操作同一数据时,可能出现脏读、不可重复读、幻读等问题。隔离级别就是为了平衡 "并发性能" 和 "数据安全性" 的配置,MySQL 有 4 种隔离级别(InnoDB 引擎)。
1. 4 种隔离级别(从低到高)

按隔离水平高低排序如下:

2. 隔离级别相关问题
脏读:读到其他事务未提交的修改(如事务 A 改了数据但没提交,事务 B 读到了这个 "临时数据",之后 A 回滚,B 读的是 "脏数据")。

因为事务 A 是还没提交事务的,也就是它随时可能发生回滚操作,如果在上面这种情况事务 A 发生了回滚,那么 事务 B 刚才得到的数据就是过期的数据,这种现象就被称为脏读。
不可重复读:同一事务内,多次查询同一数据,结果不一致。

事务 A 先开始从数据库中读取数据,然后继续执行代码逻辑处理,在这过程中如果事务 B 更新了这条数据,并提交了事务,那么当事务 A 再次读取该数据时,就会发现前后两次读到的数据是不一致的,这种现象就被称为不可重复读。
幻读:同一事务内,多次查询同一范围数据,结果集行数不一致。

事务 A 先开始从数据库查询账户余额大于 100 万的记录,发现共有 5 条, 然后事务 B 也按相同的搜索条件也是查询出了 5 条记录,接下来,事务 A 插入了一条余额超过 100 万的账号,并提交了事务,此时数据库超过 100 万余额的账号个数就变为 6。 然后事务 B 再次查询账户余额大于 100 万的记录,此时查询到的记录数量有 6 条,发现和前一次读到的记录数量 不一样了,就感觉发生了幻觉一样,这种现象就被称为幻读
3. 隔离级别的查看与设置
sql
-- 查看隔离级别(全局+会话)
SELECT @@GLOBAL.transaction_isolation; -- 全局
SELECT @@SESSION.transaction_isolation; -- 当前会话(默认REPEATABLE READ)
-- 设置隔离级别(全局/会话/仅下一个事务)
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- 仅下一个事务生效
五、隔离性的实现:锁与 MVCC
隔离级别的核心是 "隔离并发事务",而实现这一目标的两大技术是 锁机制 和 MVCC。
1. 锁机制:控制并发修改
InnoDB 的锁按粒度分为:
表级锁:锁定整个表,并发性能差(如 MyISAM 引擎默认)。
行级锁:锁定单条数据行或多行,并发性能好(InnoDB 默认,支持事务)。
按功能分为:
共享锁(S 锁):读锁,允许其他事务读取,但不允许写入(读不互斥)。
排他锁(X 锁):写锁,加锁后其他事务不能加任何锁(读写互斥、写写互斥)。
2. MVCC:多版本并发控制(非锁定读)
MVCC 是 InnoDB 实现 "读不加锁" 的关键,让读写并发不冲突:
**原理:**MVCC允许多个事务同时读取同一行数据,而不会彼此阻塞,每个事务看到的数据版本是该事务开始时的数据版本。这意味着,如果其他事务在此期间修改了数据,正在运行的事务仍然看到的是它开始时的数据状态,从而实现了非阻塞读操作。
- 读操作时,生成 ReadView(数据快照),根据隔离级别选择可见的版本:
- READ COMMITTED:每次查询生成新的 ReadView(导致不可重复读)。
- REPEATABLE READ:事务内首次查询生成 ReadView,后续复用(解决不可重复读)。
六、死锁
由于每个事务都持有另⼀个事务所需的锁,导致事务⽆法继续进⾏的情况称为死锁。

1. 死锁产生的 4 个条件
- 互斥访问:一个资源只能被一个事务占用。
- 不可抢占:事务占有的锁不能被其他事务强制夺走。
- 保持与请求:事务已持有一个锁,又请求其他事务的锁。
- 循环等待:多个事务形成锁的循环依赖(如 A 等 B 的锁,B 等 A 的锁)。
以上四条是造成死锁的必要条件,必须同时满足,所以如果想要打破死锁,可以破坏以上四个条件之一,最常见的方式就是打破循环等待
2. 避免死锁的方法
- 事务尽量短小,减少锁的持有时间。
- 修改多表 / 多行时,按固定顺序操作(如先改 A 表再改 B 表,所有事务统一顺序)。
- 避免长时间未提交的事务。
- 合理设置隔离级别(如 READ COMMITTED 可减少死锁概率)。