MySQL 事务详解:ACID 特性、隔离级别与实践
事务是数据库操作的基本单位,确保一组 SQL 语句要么全部成功执行,要么全部失败回滚,是保证数据一致性的核心机制。MySQL 中仅 InnoDB 存储引擎支持事务,本文详细解析事务的特性、隔离级别及实际应用。
事务的基本概念
事务(Transaction):由一个或多个 SQL 语句组成的执行单元,具备 “原子性”—— 要么所有语句执行成功(提交),要么全部失败(回滚),不存在部分执行的情况。
- 支持情况:MySQL 中只有
InnoDB引擎支持事务(MyISAM、MEMORY 等引擎不支持)。 - 典型场景:转账操作(扣减 A 账户金额 + 增加 B 账户金额,两步必须同时成功或失败)。
事务的 ACID 特性
事务的四大核心特性(ACID)是保证数据可靠性的基础:
| 特性 | 含义说明 | 示例 |
|---|---|---|
| 原子性(Atomicity) | 事务是不可分割的最小单位,操作要么全执行,要么全不执行。 | 转账时,若扣减 A 账户后系统崩溃,B 账户不会增加金额(事务回滚,恢复 A 账户)。 |
| 一致性(Consistency) | 事务执行前后,数据库从一个一致性状态切换到另一个一致性状态(数据规则不变)。 | 转账前 A+B 总金额为 1000,转账后总金额仍为 1000(不会多增或少减)。 |
| 隔离性(Isolation) | 多个事务并发执行时,彼此不干扰,每个事务感觉不到其他事务的存在。 | 事务 1 查询余额时,事务 2 的转账操作不会影响事务 1 的查询结果(隔离级别决定影响程度)。 |
| 持久性(Durability) | 事务提交后,对数据的修改永久生效,即使系统崩溃也不会丢失。 | 事务提交后,数据写入磁盘,重启数据库后修改仍存在。 |
事务的创建与控制
MySQL 默认开启 “自动提交”(autocommit=ON),即每条 SQL 语句自动作为一个事务提交。如需手动控制事务,需先关闭自动提交。
事务控制的核心语句
1 | -- 1. 查看自动提交状态 |
示例:手动控制事务
1 | -- 关闭自动提交 |
事务的隔离级别
当多个事务并发执行时,若隔离不当,会出现脏读、不可重复读、幻读等问题。MySQL 通过 “隔离级别” 控制事务间的可见性,平衡一致性与并发性能。
并发事务的三大问题
| 问题 | 定义 | 场景示例 |
|---|---|---|
| 脏读 | 事务 T1 读取了 T2 已修改但未提交的数据,若 T2 回滚,T1 读取的是 “无效数据”。 | T2 修改余额为 200(未提交),T1 读取到 200;T2 回滚后,T1 读取的 200 是脏数据。 |
| 不可重复读 | 事务 T1 多次读取同一数据,T2 修改并提交该数据,导致 T1 两次读取结果不一致。 | T1 首次读余额为 100,T2 修改为 200 并提交,T1 再次读为 200(前后不一致)。 |
| 幻读 | 事务 T1 读取某范围数据,T2 在该范围插入新数据并提交,T1 再次读取时多了 “幻行”。 | T1 查询 id<5 的记录(3 条),T2 插入 id=4 的记录并提交,T1 再次查询变为 4 条。 |
四种隔离级别及解决的问题
MySQL 定义了四种隔离级别,从低到高安全性递增,并发性能递减:
| 隔离级别 | 含义 | 解决的问题 | 未解决的问题 | MySQL 默认值? |
|---|---|---|---|---|
| READ UNCOMMITTED(读未提交) | 允许读取其他事务未提交的修改。 | 无 | 脏读、不可重复读、幻读 | 否 |
| READ COMMITTED(读已提交) | 只允许读取其他事务已提交的修改。 | 脏读 | 不可重复读、幻读 | 否 |
| REPEATABLE READ(可重复读) | 事务期间,多次读取同一数据结果一致(禁止其他事务修改)。 | 脏读、不可重复读 | 幻读(InnoDB 可缓解) | 是 |
| SERIALIZABLE(串行化) | 事务串行执行(加表锁),禁止并发操作。 | 所有问题 | 无(但性能极差) | 否 |
隔离级别的查看与修改
1 | -- 查看当前会话隔离级别(MySQL 8.0+) |
隔离级别的测试验证
(1)READ UNCOMMITTED(读未提交)
现象:事务 T1 修改数据未提交,T2 可立即读取到修改后的值(脏读)。
结论:几乎不使用,安全性极差。
1
2
3
4
5
6
7
8
9
10
11
12
13
14--首先设置mysql1和mysql2隔离级别为read uncommited
set session transaction isolation level read uncommitted;
--禁用mysql1 mysql2的自动提交
set autocommit = 0;
update users set u_name='李四' where id = 3;--① mysql1修改数据但未提交
select * from users where id =3;--② mysql1 查询数据 为李四
select * from users where id =3;--③ mysql2 查询数据 也为李四 查询到了mysql1中未提交的数据 出现了脏读
insert into users (u_name) values ('刘亦菲');--④mysql1 插入数据但未提交
select * from users where u_name = '刘亦菲';--⑤mysql1 查询到该数据
select * from users where u_name = '刘亦菲';--⑥mysql2 查询到该数据 出现了幻读
rollback;--⑦mysql1 回滚之前的操作 此时mysql2在查询发现之前查询到的数据都变了 出现了不可重复读
(2)READ COMMITTED(读已提交)
- 现象:T1 修改未提交时,T2 看不到;T1 提交后,T2 才能看到(解决脏读),但 T1 多次读取可能不一致(不可重复读)。
- 适用场景:对一致性要求不高,需高并发(如电商商品浏览)。
1 | --首先设置mysql1和mysql2隔离级别为read commited |
(3)REPEATABLE READ(可重复读,MySQL 默认)
- 现象:T1 启动后,多次读取同一数据结果不变(即使 T2 修改并提交),解决不可重复读;对 “幻读”,InnoDB 通过间隙锁缓解(阻止其他事务插入当前事务查询的范围数据)。
- 示例:
T1 执行SELECT * FROM users WHERE id < 10,InnoDB 会对 id<10 的范围加间隙锁,T2 无法插入 id=5 的新记录(直到 T1 提交),避免幻读。
1 | --首先设置mysql1和mysql2隔离级别为repeatable read |
(4)SERIALIZABLE(串行化)
- 现象:事务排队执行(加表锁),完全禁止并发,不会有任何并发问题,但性能极低。
- 适用场景:数据一致性要求极高(如金融核心交易),可接受低并发。
幻读的特殊处理(InnoDB 间隙锁)
MySQL 的默认隔离级别(REPEATABLE READ)下,InnoDB 通过间隙锁(Gap Lock) 缓解幻读:
当事务执行范围查询(如
WHERE id < 10)时,InnoDB 会对该范围的 “间隙”(如 id=1~9 之间的空白区域)加锁,阻止其他事务插入该范围的新数据。示例:
1
2
3
4
5-- 事务T1执行范围更新,触发间隙锁
UPDATE users SET age = 20 WHERE id < 10;
-- 事务T2尝试插入id=5的记录,会被阻塞(直到T1提交或回滚)
INSERT INTO users (id, name) VALUES (5, 'test'); -- 阻塞
保存点(Savepoint):部分回滚
保存点允许在事务中设置 “中间点”,回滚时可只撤销到该点,而非整个事务:
1 | -- 示例:使用保存点 |
- 注意:保存点仅在当前事务内有效,事务提交或回滚后自动失效。
事务的最佳实践
- 尽量缩短事务时长:减少锁持有时间,提高并发性能(避免在事务中执行无关操作,如等待用户输入)。
- 合理选择隔离级别:非核心业务用 READ COMMITTED 提高并发;核心业务用 REPEATABLE READ 或 SERIALIZABLE 保证一致性。
- 避免长事务:长事务可能导致锁积累,引发性能问题(如 InnoDB 的 undo 日志膨胀)。
- 显式控制事务:关键操作显式使用
START TRANSACTION、COMMIT、ROLLBACK,避免依赖自动提交。

v1.3.10