Skip to content

第 4 讲:事务隔离级别、脏读、不可重复读、幻读

核心结论(10 条必记)

  1. 事务隔离级别解决的是并发事务互相影响的问题
  2. 脏读是读到未提交数据
  3. 不可重复读是同一行前后读不一致
  4. 幻读是同条件范围查询结果集发生变化
  5. Read Uncommitted 隔离性最差,生产几乎不用
  6. Read Committed 可以避免脏读,但不能避免不可重复读
  7. Repeatable Read 可以避免不可重复读,是 MySQL 默认级别
  8. Serializable 隔离最强,但性能最差
  9. MVCC 主要用于 RC、RR 下的快照读
  10. MySQL 对幻读的处理是 MVCC + 锁机制共同完成的

一、并发事务的 3 类经典问题

脏读(Dirty Read)

读到另一个事务还没提交的数据。如果对方回滚,读到的就是无效数据。

T2: update balance 1000->900 (未提交)
T1: select balance -> 读到 900 (脏数据)
T2: rollback -> 恢复成 1000

不可重复读(Non-repeatable Read)

同一事务内,同一行数据前后两次读取结果不同。

T1: select balance -> 1000
T2: update balance 1000->900; commit;
T1: select balance -> 900 (同一行变了)

幻读(Phantom Read)

同一事务内,同条件范围查询结果集行数变化(多了或少了行)。

T1: select count(*) where user_id=1001 -> 5
T2: insert user_id=1001 的新订单; commit;
T1: select count(*) where user_id=1001 -> 6 (多了一行)

区别速记

问题核心表现记忆法
脏读读到未提交数据"不靠谱的数据"
不可重复读同一行前后不一致"同一行变了"
幻读范围查询行数变化"行的数量变了"

二、四种事务隔离级别

隔离级别脏读不可重复读幻读说明
Read Uncommitted可能可能可能最弱,生产几乎不用
Read Committed (RC)不会可能可能Oracle 默认
Repeatable Read (RR)不会不会理论可能,MySQL 控制更强MySQL InnoDB 默认
Serializable不会不会不会最强,但并发性能差

隔离级别越高,一致性越强,但并发性能越低


三、RC vs RR 的核心区别

Read Committed (RC)

  • 每次读取生成新的 Read View
  • 读的是当前最新已提交版本
  • 可能不可重复读(因为每次读都看最新)

Repeatable Read (RR)

  • 事务第一次快照读时生成 Read View,之后一直复用
  • 事务内多次读取结果一致
  • 避免不可重复读
RC: 每次读 -> 新 Read View -> 可能看到别人已提交的更新
RR: 首次读 -> 固定 Read View -> 事务内始终一致

四、MySQL 为什么默认用 RR?

  1. 一致性更强 -- 比 RC 多解决不可重复读
  2. MVCC 支撑 -- InnoDB 能高效实现 RR,性能损失不大
  3. 业务需要 -- 订单/库存/账户类业务希望事务内视图稳定

五、快照读 vs 当前读

类型SQL 示例特点
快照读select * from user where id = 1走 MVCC,不加锁,读某个可见版本
当前读select ... for update / update / delete / insert读最新版本,加锁,考虑锁冲突

MVCC 服务的对象

  • 主要服务于 RC、RR 下的快照读
  • 当前读要靠锁机制

六、RR 下幻读的争议

快照读场景

RR 借助 MVCC,第二次普通 select 通常看不到别人后插入的新行 -> 看起来避免了幻读

当前读场景

select ... for update / update 等读最新数据,需要靠间隙锁 + 临键锁阻止范围内插入新行

面试回答

标准 SQL 角度 RR 不能完全解决幻读,Serializable 才能彻底解决。MySQL InnoDB 中,普通快照读靠 MVCC 很多场景下看起来避免了幻读;当前读场景还需通过间隙锁、临键锁防止范围内插入,进一步控制幻读问题。


七、MySQL 并发控制的两条线

普通读一致性问题 -> 靠 MVCC 解决
  RC: 每次读新快照
  RR: 事务内复用快照

当前读下的并发插入/修改问题 -> 靠锁解决
  行锁 / 间隙锁 / 临键锁

MySQL 的事务隔离不是只靠 MVCC,也不是只靠锁,而是两者结合


八、面试高频题

1. 脏读/不可重复读/幻读区别?

脏读: 读到未提交数据 不可重复读: 同一事务内同一行前后读结果不同 幻读: 同一事务内同条件范围查询结果集行数变化

2. MySQL 默认隔离级别?

InnoDB 默认 Repeatable Read(RR)

3. RC 和 RR 区别?

RC 每次读已提交最新版本,可能不可重复读 RR 事务内复用同一个 Read View,避免不可重复读,一致性更强

4. RR 能完全解决幻读吗?

标准角度不能完全解决。MySQL 中快照读靠 MVCC,当前读靠间隙锁/临键锁共同控制。


练习题(待完成)

  • [ ] 练习 1:用账户余额举例说明脏读
  • [ ] 练习 2:不可重复读和幻读的区别
  • [ ] 练习 3:为什么 RC 会出现不可重复读,而 RR 不会?
  • [ ] 练习 4:MySQL 默认为什么用 RR 而不是 RC?

下一讲预告

第 5 讲:MVCC、Read View、快照读、当前读

  • MVCC 具体实现机制
  • 隐藏字段是什么
  • undo log 版本链怎么形成
  • Read View 是什么
  • RC 和 RR 为什么行为不同
  • 快照读和当前读到底怎么区分

基于 VitePress 构建