脏读、幻读
# 一、四个隔离级别是什么?
| 隔离级别 | 英文名 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|---|
| 读未提交 | READ UNCOMMITTED | ✅ 可能 | ✅ 可能 | ✅ 可能 |
| 读已提交 | READ COMMITTED | ❌ 不会 | ✅ 可能 | ✅ 可能 |
| 可重复读 | REPEATABLE READ | ❌ 不会 | ❌ 不会 | ✅ 可能(MySQL InnoDB 通过 MVCC 避免) |
| 串行化 | SERIALIZABLE | ❌ 不会 | ❌ 不会 | ❌ 不会 |
隔离级别越严格,性能越差,数据一致性越好。
# 二、场景
# 1️⃣ 脏读(Dirty Read):读到了别人还没提交的数据
场景: 事务 A 修改了余额,但还没提交;事务 B 读到了这个未提交的数据。
| 时间 | 事务 A | 事务 B |
|---|---|---|
| T1 | BEGIN; | |
| T2 | UPDATE account SET balance=800 WHERE id=1;(还没提交) | 读未提交 |
| T3 | BEGIN; | |
| T4 | SELECT balance FROM account WHERE id=1; → 读到了 800 | |
| T5 | ROLLBACK;(回滚了,余额变回 1000) | |
| T6 | 事务 B 一直以为余额是 800,实际上错了 |
结论: 事务 B 读到了一条根本不存在的数据(回滚后就没了),这就是脏读。
读未提交允许脏读,读已提交及以上不允许。
# 2️⃣不可重复读(Non-Repeatable Read):两次读同一条数据,结果不一样
场景: 事务 A 修改并提交了数据;事务 B 在同一事务中两次查询,结果不同。
| 时间 | 事务 A | 事务 B |
|---|---|---|
| T1 | BEGIN; | |
| T2 | SELECT balance FROM account WHERE id=1; → 1000 | |
| T3 | BEGIN; | |
| T4 | UPDATE account SET balance=800 WHERE id=1; COMMIT; | 读已提交 |
| T5 | SELECT balance FROM account WHERE id=1; → 800 | |
| T6 | COMMIT; |
结论: 同一事务内,查询同一条数据,前后结果不一致(1000 → 800),这就是不可重复读。
读已提交允许不可重复读,可重复读及以上不允许。
# 3️⃣ 幻读(Phantom Read):两次查询的行数不一样
场景: 事务 A 插入了一条新数据并提交;事务 B 在同一个事务中两次查询,行数不同。
| 时间 | 事务 A | 事务 B |
|---|---|---|
| T1 | BEGIN; | |
| T2 | SELECT * FROM account WHERE name LIKE '张%'; 1 行(张三) | |
| T3 | BEGIN; | |
| T4 | INSERT INTO account VALUES (2,'张四',500); COMMIT; | |
| T5 | SELECT * FROM account WHERE name LIKE '张%'; 2 行(张三、张四) | |
| T6 | COMMIT; |
结论: 同一事务内,同一个查询条件,返回的记录数量变了,这就是幻读。
可重复读在 MySQL InnoDB 中通过 MVCC(多版本并发控制)或间隙锁(Gap Lock)来避免幻读,但其他数据库(如 PostgreSQL)的默认隔离级别可能需要更高设置。
# 三、不同隔离级别能解决什么问题?
| 隔离级别 | 能解决什么问题? | 典型使用场景 |
|---|---|---|
| 读未提交 | 什么都不解决,性能最高 | 几乎不用,数据一致性太差 |
| 读已提交 | 解决脏读 | 大多数 OLTP 系统(如电商订单),追求性能且可接受短暂不一致 |
| 可重复读 | 解决脏读 + 不可重复读 | MySQL 默认级别,适合报表类业务,对数据一致性要求较高的场景 |
| 串行化 | 解决所有问题(脏读、不可重复读、幻读) | 极少数对一致性要求极高且并发低的场景(如银行核心账务) |
# 四、快速记忆口诀
读未提交,什么都读(脏、幻、不可重复全都有) 读已提交,只能读到提交的(脏读没了,但幻读和不可重复读还在) 可重复读,读到的始终一样(脏读和不可重复读没了,MVCC 下幻读也没了) 串行化,统统排队(所有问题都解决,但性能最差)
# 五、一句话总结
- 脏读 = 读到别人没提交的垃圾数据
- 不可重复读 = 读同一行数据,前后值不一样
- 幻读 = 查询同一张表,前后行数不一样
隔离级别从低到高,一步步堵住这些漏洞。
上次更新: 2026-06-25 17:18:05