深入理解 InnoDB 的 MVCC:原理、Read View 与可见性判断
摘要
多版本并发控制(Multi-Version Concurrency Control,MVCC)是 InnoDB 实现高并发读的核心机制之一:读操作通常不需要加行锁即可读到「一致性快照」,写操作通过版本链与 undo log 维护历史版本。本文从行记录隐藏字段、undo 版本链、Read View 与可见性算法入手,区分快照读与当前读,并辅以流程图梳理判断逻辑,最后简要讨论隔离级别与 MVCC 的边界。
一、为什么需要 MVCC
在传统「读写互斥」模型下,读多写少的 OLTP 场景容易因锁竞争拖慢查询。MVCC 的思路是:为每行数据维护多个版本,事务读取时根据规则选择其中一个版本,从而:
- 普通读(快照读)多数情况下避免对数据行加锁,减少阻塞;
- 写仍通过锁与日志保证正确性,与读在版本维度上解耦。
InnoDB 的 MVCC 主要服务于读已提交(RC)与可重复读(RR) 下的快照读;串行化等场景会更多依赖锁。
二、InnoDB 行记录与隐藏列
聚簇索引叶子节点中的每行(逻辑上)除用户列外,还包含与 MVCC 相关的隐藏字段(概念上):
| 隐藏字段 | 含义 |
|---|---|
DB_TRX_ID |
最近一次插入或更新该行的事务 ID |
DB_ROLL_PTR |
指向 undo log 中旧版本行的指针(回滚指针) |
DB_ROW_ID |
无主键/非空唯一索引时用于生成聚簇索引行 ID(与 MVCC 可见性无直接关系) |
更新一行时,InnoDB 会生成 undo 记录,旧值通过 DB_ROLL_PTR 连成版本链,新版本成为当前行上的「当前版本」。
三、undo log 与版本链(简图)
undo 链(从新到旧)
当前行(聚簇索引页)
Row: trx_id, roll_ptr → ...
Undo1
Undo2
Undo3
读请求不会随意遍历整条物理链到「无穷旧」,而是通过 Read View 决定在逻辑上哪一版对该事务可见。
四、Read View 是什么
Read View 是某次快照读建立的一份「一致性读视图」,其中保存了生成时刻的事务 ID 快照信息,用于判断:当前行某版本的 trx_id 对本读事务是否可见。
典型实现中会关心(不同版本源码字段名可能略有差异,语义一致):
- 活跃事务 ID 列表
m_ids:生成 Read View 时仍处于活跃状态的事务集合; - 最小活跃事务 ID
min_trx_id; - 系统分配给下一个事务的 ID
max_trx_id(或等价上界); - 创建该 Read View 的事务 ID
creator_trx_id。
五、可见性判断核心流程
对某行版本,其事务 ID 记为 trx_id,当前 Read View 记为 RV。下面给出教学中常用的判定顺序(与源码细节顺序一致即可,不必死记变量名)。
是
否
是
否
是
否
是
否
取该行版本的 trx_id
trx_id == creator_trx_id?
可见:本事务自己的修改
trx_id < RV.min_trx_id?
可见:在 RV 创建前已提交
trx_id >= RV.max_trx_id?
不可见:晚于 RV 的事务产生
trx_id 在 m_ids 中?
不可见:活跃未提交
可见:已提交且不在活跃集
沿 roll_ptr 取上一版本继续判断
返回该版本数据
要点:
- 若当前版本对读事务不可见,则沿
DB_ROLL_PTR向 undo 方向找更旧的版本,直到可见或链结束。 - RC 与 RR 的重要差异之一:在 InnoDB 中,同一事务内 RC 往往每条语句 可能使用新的 Read View;RR 则在首次快照读时固定 Read View(具体以官方文档与当前版本行为为准),因此出现「不可重复读」与「可重复读」的差异。
六、快照读 vs 当前读
| 类型 | 典型语句 | 是否走 MVCC 快照 |
|---|---|---|
| 快照读 | 普通 SELECT(无锁读) |
是,按 Read View 选版本 |
| 当前读 | SELECT ... FOR UPDATE/LOCK IN SHARE MODE、UPDATE、DELETE |
读最新已提交版本并加锁(语义需要看到最新) |
因此:MVCC 解决的是「一致性快照读」的路径 ;当前读仍依赖锁与最新版本,和「避免幻读」等话题要结合 Next-Key Lock 等机制一起看(RR 下范围锁等)。
七、与隔离级别的关系(概念对齐)
隔离级别
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
- RC:语句级一致性读视图(多次 SELECT 可能看到别的事务新提交)。
- RR :事务级一致性快照(同一事务多次 SELECT 看到同一快照;InnoDB 下对幻行还有间隙锁等配合,需区分「快照可见性」与「锁防插入」)。
八、MVCC 的边界与常见误解
- MVCC 不是「完全无锁」:写操作、当前读仍可能加锁;垃圾回收、purge 线程清理旧版本也有成本。
- 长事务:长时间不提交的事务可能使旧版本无法及时 purge,导致 undo 膨胀与空间压力。
- 只读事务:若能明确只读,可合理使用「一致性快照」语义;仍要注意元数据锁、DDL 等其它因素。
九、小结
- MVCC 通过 trx_id + undo 版本链 + Read View 实现快照读路径上的一致性视图。
- 可见性 由 Read View 与版本
trx_id比较决定,不可见则沿roll_ptr回溯。 - 区分 快照读 与 当前读 ,并结合 RC/RR 理解「何时换 Read View」。
- 生产环境需关注 长事务、purge、索引设计 等与版本堆积相关的问题。