AI 模拟面试实战:MySQL MVCC、ReadView 与 Undo Log 版本链的白板推演
在各大厂的后端面试中,MySQL 的多版本并发控制(MVCC,Multi-Version Concurrency Control)堪称“面试八股之王”。
几乎所有候选人都能背出:“MVCC 靠的是隐藏字段、Undo Log 版本链和 ReadView”。
但一旦面试官在白板上画出 3 个事务的并发时序图,并给出一张包含trx_id的记录行,让你推演“事务 A 在可重复读(RR)与读已提交(RC)下分别能看到哪个版本的值”,80% 的同学都会在可见性算法的条件判断上发生混乱。
今天我们通过 AI 模拟面试官的硬核追问,结合真实案例推演 MVCC 的底层判定流程。
MVCC 底层三大基石解构
- 三条隐藏系统字段:
DB_TRX_ID(6 字节):记录最后一次插入或修改该行记录的事务 ID;DB_ROLL_PTR(7 字节):回滚指针,指向写入undo log的上一个历史版本记录;DB_ROW_ID(6 字节):若表没有主键和唯一索引,InnoDB 自动生成的隐藏聚簇索引 ID。
- Undo Log 版本链:
每次执行UPDATE或DELETE时,旧版本数据被写入 Undo Log,并通过DB_ROLL_PTR指针串联成一条从最新版本指向最老版本的单向链表。 - 一致性视图 ReadView:
事务在执行快照读(SELECT)时生成的当前活跃事务快照,包含 4 个核心属性:m_ids:生成 ReadView 瞬间,系统当前正在活跃且未提交的事务 ID 列表;min_trx_id:m_ids中的最小值;max_trx_id:生成 ReadView 瞬间,系统即将分配给下一个新事务的 ID(即当前已分配最大事务 ID + 1);creator_trx_id:创建当前 ReadView 的事务自身的 ID。
ReadView 可见性判定算法流程
当事务去访问 Undo Log 版本链上的某个记录版本时,提取该版本的trx_id,按以下规则判定是否可见:
graph TD A[提取版本 trx_id] --> B{trx_id == creator_trx_id ?} B -->|是| C[可见: 是当前事务自己修改的] B -->|否| D{trx_id < min_trx_id ?} D -->|是| E[可见: 说明该版本在生成 ReadView 前已提交] D -->|否| F{trx_id >= max_trx_id ?} F -->|是| G[不可见: 说明该版本在生成 ReadView 后才开启] F -->|否| H{trx_id 在 m_ids 活跃列表中?} H -->|是| I[不可见: 说明生成 ReadView 时该事务尚未提交] H -->|否| J[可见: 说明该事务已提交] G --> K[沿 DB_ROLL_PTR 查找上一版本继续判定] I --> K白板实操推演案例
假设数据表中有一行记录:id = 1, name = '张三', DB_TRX_ID = 50(已提交)。
并发发生如下时序操作:
- 10:00:00,事务 100 开启,更新为
name = '李四'(未提交); - 10:00:01,事务 200 开启,更新为
name = '王五'(未提交); - 10:00:02,事务 300 开启并提交:更新为
name = '赵六'; - 10:00:03,事务 400 开启,执行第一次
SELECT。
此时版本链状态:[赵六 (trx_id=300)] -> [王五 (trx_id=200)] -> [李四 (trx_id=100)] -> [张三 (trx_id=50)]
事务 400 生成 ReadView:m_ids = [100, 200], min_trx_id = 100, max_trx_id = 401, creator_trx_id = 400
推演查询流程:
- 访问最新版本
赵六 (trx_id=300):
$300 \ge 100$ 且 $300 < 401$,检查是否在m_ids [100, 200]中 $\to$不在!说明事务 300 已经提交。
$\to$判定结果:事务 400 可以直接读到name = '赵六'!
RC 隔离级别与 RR 隔离级别的核心差异
在向 AI 追问“RC 与 RR 在 MVCC 实现上的本质区别是什么”时,大模型精准提炼出了那句价值千金的话:
- 读已提交(RC,Read Committed):
每次执行快照读SELECT时,都会重新生成一个全新的 ReadView。因此如果其他事务中途提交,第二次SELECT就能看到新提交的数据,从而产生不可重复读。 - 可重复读(RR,Repeatable Read):
仅在事务中的第一次执行快照读SELECT时生成 ReadView,后续整个事务生命周期内一直复用该 ReadView。因此无论其他事务后续如何提交,本事务看到的永远是第一次查询时锁定的快照状态,完美保证了可重复读。
实习生面试总结
在回答 MVCC 题目时,千万不要只背术语。主动在纸上写出[min_trx_id, max_trx_id)区间划分,画出DB_ROLL_PTR链条,并指出 RC 与 RR 生成 ReadView 的时机差异,面试官对你的技术评价立刻就会上升到资深档次。