InnoDB 的 MVCC(多版本并发控制)机制通过维护数据的多个历史版本,结合事务隔离级别的规则,实现了高效的并发控制。其核心实现依赖于 隐藏字段 、Undo Log 和 Read View 三大组件,具体工作原理如下:
一、核心组件与作用
-
隐藏字段
每行数据包含三个隐藏字段(非用户定义):
-
DB_TRX_ID(6字节):记录最后一次修改该行的事务ID。
-
DB_ROLL_PTR(7字节):回滚指针,指向该行在Undo Log中的历史版本。
-
DB_ROW_ID(6字节):若表无主键,InnoDB用此字段生成聚簇索引。
这些字段用于标识数据版本和构建版本链。
-
-
Undo Log(回滚日志)
-
存储数据修改前的旧版本,形成版本链(通过DB_ROLL_PTR串联)。
-
分为两类:
-
Insert Undo Log:事务提交后可直接删除(仅对插入事务可见)。
-
Update Undo Log:需保留至无事务需要访问旧版本(由Purge线程清理)。
用于事务回滚和MVCC的快照读。
-
-
-
Read View(读视图)
事务执行快照读时生成的一致性视图,包含:
-
m_ids
:当前活跃(未提交)的事务ID列表。 -
min_trx_id
:活跃事务中的最小ID。 -
max_trx_id
:下一个待分配的事务ID。 -
creator_trx_id
:创建该Read View的事务ID。通过比较数据行的DB_TRX_ID与Read View属性,判断版本可见性。
-
二、MVCC工作流程
1. 读操作(SELECT)
-
快照读(普通SELECT):
-
获取当前事务的Read View。
-
从数据行的最新版本开始,沿Undo Log版本链回溯。
-
根据可见性规则找到第一个符合条件的历史版本:
- 若
DB_TRX_ID < min_trx_id
:版本已提交,可见。 - 若
DB_TRX_ID
在m_ids
中:版本未提交,不可见。 - 若
DB_TRX_ID == creator_trx_id
:当前事务自身修改,可见。
- 若
-
-
当前读 (如
SELECT ... FOR UPDATE
):直接读取最新版本并加锁,不依赖MVCC。
2. 写操作(INSERT/UPDATE/DELETE)
-
INSERT :新记录写入,
DB_TRX_ID
设为当前事务ID,DB_ROLL_PTR
为空。 -
UPDATE/DELETE:
- 将旧数据拷贝到Undo Log,生成新版本。
- 更新当前行的
DB_TRX_ID
和DB_ROLL_PTR
(指向Undo Log中的旧版本)。 - DELETE操作标记删除位,不立即物理删除。
三、隔离级别的实现差异
-
READ COMMITTED (RC)
- 每次SELECT生成新的Read View,能看到其他事务已提交的最新修改。
- 解决脏读,但可能出现不可重复读。
-
REPEATABLE READ (RR,InnoDB默认)
- 仅在第一次SELECT时生成Read View,后续复用该视图。
- 保证事务内多次读取数据一致,避免不可重复读。
- 幻读问题 :MVCC本身无法完全解决,需配合间隙锁(Next-Key Lock)。
四、MVCC的优势与代价
优势 | 代价 |
---|---|
✅ 读写不阻塞(读历史版本,写锁当前版本) | ❌ 存储开销(Undo Log保留多版本) |
✅ 高并发(减少锁竞争) | ❌ 版本链遍历增加CPU开销 |
✅ 天然支持RC和RR隔离级别 | ❌ 长事务阻碍旧版本清理(Purge延迟) |
五、总结
InnoDB的MVCC通过版本链 和快照读机制,实现了非阻塞的并发控制:
- 读操作 访问历史版本,避免锁竞争;写操作生成新版本,不影响读操作。
- 隔离级别通过Read View的生成时机(RC每次生成,RR首次生成)实现不同一致性视图。
- 幻读需结合间隙锁解决,体现MVCC与锁机制的协同设计。
该机制是InnoDB高并发能力的核心,但也需合理管理事务时长和版本清理以避免性能下降。