Skip to content

P33 MySQL 的隔离级别实现原理 MVCC ​

面试题:MySQL 的四种隔离级别是什么?MVCC 是怎么实现隔离的?

1. 四种隔离级别 ​

隔离级别脏读不可重复读幻读
读未提交(READ UNCOMMITTED)可能可能可能
读已提交(READ COMMITTED)解决可能可能
可重复读(REPEATABLE READ,MySQL 默认)解决解决基本解决(InnoDB)
串行化(SERIALIZABLE)解决解决解决

InnoDB 的 RR 级别通过 MVCC + 间隙锁 额外解决了幻读(标准 SQL 里 RR 是允许幻读的,这是面试易错点)。

2. 三个并发问题的定义 ​

  • 脏读:读到其他事务未提交的数据;
  • 不可重复读:同一事务内两次读同一行,结果不一样(其他事务提交了修改);
  • 幻读:同一事务内两次范围查询,行数不一样(其他事务插入了新行)。

3. MVCC 的核心结构 ​

MVCC(多版本并发控制)= 每行记录保存多个版本,读的时候只看自己事务可见的版本,读写互不阻塞。

① 隐藏字段 ​

InnoDB 每行有三个隐藏列:

  • DB_TRX_ID:最近一次修改该行的事务 ID;
  • DB_ROLL_PTR:回滚指针,指向 undo log 里的旧版本;
  • DB_ROW_ID:无主键时的隐藏主键。

② undo log 版本链 ​

每次修改不是覆盖旧值,而是把旧值链到 undo log,形成版本链:

text
最新版本 → 旧版本1 → 旧版本2 → ...

③ ReadView(读视图) ​

事务开始快照读时生成 ReadView,记录:

  • 当前未提交事务的最小 ID(m_ids 中最小);
  • 已创建的最大事务 ID;
  • 当前事务自己的 ID。

判断版本可见性:某版本的事务 ID 在 ReadView 的活跃列表里,则不可见,沿版本链找上一个版本;直到找到"生成 ReadView 时已提交"的版本。

4. RC 与 RR 的区别(面试必问) ​

  • RC(读已提交):每次 SELECT 都生成新的 ReadView → 能看到其他事务新提交的数据 → 不可重复读;
  • RR(可重复读):事务第一次快照读时生成 ReadView,整个事务复用 → 读到的永远是快照时刻的数据 → 可重复读。

这就是两种隔离级别在 MVCC 下的本质差别:ReadView 的生成时机。

5. 当前读与加锁 ​

MVCC 解决的是快照读;SELECT ... FOR UPDATE、UPDATE、DELETE 是当前读,读最新版本并加锁:

  • 行锁(Record Lock)+ 间隙锁(Gap Lock)+ 临键锁(Next-Key Lock)防幻读;
  • 所以 RR 下靠"MVCC(快照读)+ Next-Key 锁(当前读)"双保险。

一句话总结 ​

MySQL 默认 RR 隔离;MVCC 通过隐藏列(事务ID+回滚指针)+ undo 版本链 + ReadView 让快照读不阻塞,RC 每次 SELECT 新建 ReadView、RR 复用首次 ReadView,因此 RR 可重复读;当前读再配合 Next-Key 锁解决幻读。

基于 VitePress 重建