MVCC 的难点不在“多版本并发控制”这个名字,而在可见性判断。普通 select 读到的并不总是最新行,而是对当前 ReadView 可见的那一版记录。
它带来的收益是读写在很多场景下不用互相阻塞,代价是历史版本、undo 链和长事务治理都变得重要。这一篇抓住 ReadView、trx_id 和 roll_pointer 三个点,就能把快照读的行为串起来。
先把机制边界说清楚
MVCC 通过记录版本和可见性规则,让读写在很多场景下不互相阻塞。InnoDB 记录里有事务 id 和回滚指针;ReadView 描述当前事务能看见哪些事务的提交结果;undo 链提供旧版本。三者组合起来,形成快照读。
整体路径
上面这张图先看入口和边界:宏观上,快照读先读取当前记录版本,判断该版本的 trx_id 是否对自己的 ReadView 可见。可见就返回;不可见就沿 roll_pointer 找到上一个 undo 版本继续判断,直到找到可见版本或确认不存在。
第二张图再看结构关系。
底层流程
底层拆解先看数据结构。「MVCC」至少涉及下面几类结构:
- ReadView:包含活跃事务集合、低水位和高水位,用于判断版本可见性。
- trx_id:每次修改记录时写入的事务编号。
- roll_pointer:指向上一版本 undo 记录。
- undo 版本链:保存历史版本,让快照读能回到过去。
- 当前读:加锁读、更新和删除读取最新已提交或可锁定版本。
再看完整执行流程:
- 事务发起快照读并获取或复用 ReadView。
- InnoDB 读取记录当前版本。
- 用 ReadView 判断记录 trx_id 是否可见。
- 不可见时沿 undo 链读取上一个版本。
- 找到可见版本后返回给 Server 层。
取舍与边界
版本差异上,MySQL 5.7 和 8.0 的 MVCC 主体规则一致。差异更多体现在可观测性、undo 管理和优化器能力。RC 隔离级别通常每条语句生成新的 ReadView,RR 隔离级别在事务内复用第一次快照读的 ReadView。
MVCC 的短板是它只解决一致性读,不替代锁。当前读仍然要面对行锁和间隙锁;长事务会保留旧 ReadView,拖住 undo 清理;版本链太长时,快照读也会变慢。
典型问题:用机制化例子排查
“读不到最新数据”不一定是数据库没更新,也可能是同一个事务一直在使用同一个 ReadView。排查时先确认这是快照读还是当前读,再看事务边界和隔离级别。
可以落到这些动作:
- 区分快照读和当前读,不要把普通 select 的行为套到 update/select for update 上。
- 根据业务一致性选择 RC 或 RR,别只沿用默认值。
- 对长事务设置超时和告警,避免拖住 undo。
- 遇到“读不到最新数据”时先检查事务边界和隔离级别。
收束:先问读的是哪个时间点
MVCC 的价值是让读写少互相阻塞,但它要求工程师理解“我读的是哪个时间点”。时间点不清楚,一致性问题就会变成玄学。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 MySQL」,欢迎关注公众号 「十三Tech」。后续会继续按机制、图解和实战排查这条线更新。

