Multi-Version Concurrency Control 多版本并发控制
InnoDB 为每行记录添加了两个隐藏列:DB_TRX_ID(事务ID)和 DB_ROLL_PTR(回滚指针),用于实现多版本。
记录数据修改前的值,形成版本链。每次修改都会生成一条 undo 记录,通过回滚指针串联起来。
读事务开始时创建的快照,记录当前活跃事务ID列表,用于判断数据版本的可见性。
根据 Read View 判断:数据的事务ID是否对当前事务可见,决定读取哪个版本的数据。
每一行数据背后,都隐藏着 MVCC 的关键信息:
DB_TRX_ID:记录最近一次修改该行的事务ID
DB_ROLL_PTR:指向 undo log 记录的指针,形成版本链的链表头
当事务读取一行数据时,通过 Read View 判断该行数据的某个版本是否可见:
IF (trx_id == creator_trx_id) THEN
// 自己修改的,可以看见
ELSE IF (trx_id < min_trx_id) THEN
// 提交过且比快照还早,可以看见
ELSE IF (trx_id > max_trx_id) THEN
// 快照创建后才开始的,看不见
ELSE IF (trx_id in m_ids) THEN
// 活跃事务修改的,看不见
→ 通过 roll_ptr 顺着版本链往下找...
| 时间 | 事务A (转账) | 事务B (查询) | 账户余额 | 说明 |
|---|---|---|---|---|
| T1 | BEGIN | - | 1000 | 初始余额 |
| T2 | UPDATE SET balance=900 | - | - | 事务A修改,未提交 |
| T3 | - | BEGIN → SELECT | 1000 | 读到了旧版本! |
| T4 | COMMIT | - | 900 | 事务A提交 |
| T5 | - | SELECT again | 1000 | RC下新快照,RR下仍1000 |
| T6 | - | COMMIT | 900 | 事务B提交,余额900 |
• 读不阻塞写,写不阻塞读
• 大幅提升并发性能
• 快照读避免幻读问题
• 减少锁争用
• 需要定期清理旧版本(purge)
• 极端情况下可能产生长事务
• RR 级别下仍有幻读可能(需 gap lock)
• 只适用于快照读
• 高并发读多写少
• 需要一致性快照
• 长事务处理
• 报表查询