MySQL 事务隔离级别与 MVCC 实现原理详解

在 MySQL 面试中,事务隔离级别和 MVCC(多版本并发控制)几乎是必问的考点。很多候选人对“可重复读”“幻读”等概念倒背如流,但一旦追问“MVCC 底层怎么实现的”“Read View 里到底存了什么”,往往就答不上来了。这篇文章从实战和面试的角度,把这两个知识点彻底讲清楚。

一、事务隔离级别:四种级别与三种读问题

SQL 标准定义了四种隔离级别,MySQL 的 InnoDB 引擎全部支持:

隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED 可能 可能 可能
READ COMMITTED (RC) 不可能 可能 可能
REPEATABLE READ (RR) 不可能 不可能 InnoDB 下基本不可能
SERIALIZABLE 不可能 不可能 不可能

三种读问题的本质区别在于**“两次读之间,其他事务做了什么”**:

  • 脏读:读到了其他事务未提交的数据。
  • 不可重复读:同一行数据两次读取结果不同(其他事务提交了 UPDATE)。
  • 幻读:同一范围两次查询,第二次多出或少了一些行(其他事务提交了 INSERT/DELETE)。

MySQL 的默认隔离级别是 REPEATABLE READ。值得注意的是,标准 RR 并不能防止幻读,但 InnoDB 通过 Next-Key Lock(间隙锁 + 行锁) 在 RR 下基本解决了幻读问题。

二、MVCC 的核心概念

MVCC(Multi-Version Concurrency Control)的核心思想是:读操作不加锁,通过读取历史版本实现一致性读。它依赖三个隐藏组件:

1. 隐藏字段

InnoDB 为每行记录添加两个隐藏列:

  • DB_TRX_ID(6 字节):最近一次修改该行的事务 ID。
  • DB_ROLL_PTR(7 字节):回滚指针,指向 undo log 中的旧版本记录。

2. Undo Log 版本链

每次对记录做修改时,旧值会被写入 undo log,并通过 DB_ROLL_PTR 串联成一条版本链。链头是最新版本,链尾是最老的版本。

当前行 → undo log v3 → undo log v2 → undo log v1

3. Read View(读视图)

Read View 是 MVCC 实现一致性读的关键,它决定了一个事务能看到版本链中的哪个版本。Read View 包含四个核心字段:

  • m_ids:生成 Read View 时,当前活跃(未提交)的事务 ID 列表。
  • min_trx_id:m_ids 中的最小值。
  • max_trx_id:生成 Read View 时,系统应分配给下一个事务的 ID(即当前最大事务 ID + 1)。
  • creator_trx_id:创建该 Read View 的事务自身 ID。

三、可见性判断规则

拿到版本链中某个版本的 DB_TRX_ID 后,按以下规则判断是否可见:

  1. trx_id == creator_trx_id:说明是自己修改的,可见
  2. trx_id < min_trx_id:说明该版本在 Read View 创建前已提交,可见
  3. trx_id >= max_trx_id:说明该版本在 Read View 创建后才开启,不可见
  4. min_trx_id <= trx_id < max_trx_id
    • 若 trx_id 在 m_ids 中,说明事务仍活跃,不可见
    • 若不在 m_ids 中,说明已提交,可见

如果当前版本不可见,就顺着 DB_ROLL_PTR 找上一个版本,直到找到可见版本或链尾。

四、RC 与 RR 的本质区别

这是面试的高频追问点:RC 和 RR 都基于 MVCC,为什么行为不同?

答案在于 Read View 的生成时机

  • READ COMMITTED每次 SELECT 都生成一个新的 Read View。所以每次查询都能看到最新已提交的数据,导致不可重复读。
  • REPEATABLE READ只在事务第一次 SELECT 时生成一个 Read View,整个事务复用。所以后续查询看到的仍是同一快照,实现可重复读。

五、当前读 vs 快照读

MVCC 只作用于快照读(普通 SELECT)。以下操作属于当前读,读取的是最新版本,并加锁:

  • SELECT ... FOR UPDATE
  • SELECT ... LOCK IN SHARE MODE
  • INSERT / UPDATE / DELETE

在 RR 下,当前读配合 Next-Key Lock 来防止幻读。这也是为什么“RR 下 MVCC + 间隙锁”能基本解决幻读的原因。

六、常见面试追问

Q1:RR 下幻读真的完全解决了吗?
没有完全解决。快照读靠 MVCC 避免幻读,当前读靠间隙锁避免幻读。但如果在快照读之后执行了当前读(如 UPDATE),可能看到“多出来”的行,这就是所谓的“幻读的边界情况”。

Q2:undo log 什么时候删除?
当没有比该版本更早的 Read View 需要它时,由 purge 线程清理。长事务会阻止清理,导致 undo log 膨胀。

Q3:为什么不用锁实现一致性读?
加锁会阻塞写操作,并发性能差。MVCC 让读不加锁、写不阻塞读,极大提升了并发能力。

七、总结

一句话概括:MVCC 通过隐藏字段 + undo log 版本链 + Read View 实现无锁一致性读;RC 与 RR 的区别只在于 Read View 的生成时机。 理解这条主线,再结合当前读、间隙锁等概念,MySQL 事务相关的面试题基本就能从容应对了。

未经允许不得转载:任鹏个人博客 » MySQL 事务隔离级别与 MVCC 实现原理详解

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏