InnoDB 存储引擎对 MVCC 的实现详解
MVCC(Multi-Version Concurrency Control,多版本并发控制)是 InnoDB 实现事务隔离级别和提高并发性能的关键机制。它允许数据库在 不加锁的情况下进行一致性读,提高读写并发性能。
一、什么是 MVCC?
MVCC 的核心思想:
- 每条记录(行)保留多个版本(通过隐藏列和 Undo Log)。
- 事务读取数据时,读取满足其 一致性视图(Read View) 的版本,而不是最新版本。
- 这样可以避免大部分读操作加锁,减少阻塞。
目标:
- 实现 快照读(非锁定读),提高并发性能。
- 保证事务隔离级别,特别是 READ COMMITTED 和 REPEATABLE READ。
二、InnoDB 实现 MVCC 的核心机制
InnoDB 的 MVCC 主要通过以下机制实现:
- 隐藏列
- Undo Log(回滚日志)
- Read View(读视图)
1. 隐藏列
InnoDB 每行记录都有两个隐藏列(实际上是三列):
- DB_TRX_ID:最后修改该行的事务 ID。
- DB_ROLL_PTR:回滚指针,指向 Undo Log,记录旧版本。
- DB_ROW_ID(可选):自增 ID(如果没有显式主键)。
2. Undo Log(回滚日志)
Undo Log 保存了 行数据的历史版本,用于:
- MVCC 版本链(保证一致性读)。
- 事务回滚。
版本链结构:
- 每次 UPDATE 或 DELETE,都会把 旧版本 写入 Undo Log,并用 回滚指针(DB_ROLL_PTR) 链接。
- 这样就形成一条 历史版本链。
示意图:
最新版本(当前行) → Undo Log V1 → Undo Log V2 → ...
3. Read View(读视图)
Read View 是事务执行一致性读时创建的视图,决定事务可以“看到”哪些版本。
包含以下关键字段:
- m_ids:当前活跃事务 ID 列表。
- min_trx_id:活跃事务中最小的事务 ID。
- max_trx_id:下一个将分配的事务 ID。
- creator_trx_id:创建视图的事务 ID。
版本可见性规则:
- 如果版本的
DB_TRX_ID
小于 min_trx_id → 可见(已提交)。 - 如果版本的
DB_TRX_ID
大于 max_trx_id → 不可见(未开始)。 - 如果版本的
DB_TRX_ID
在活跃事务列表中 → 不可见(未提交)。 - 否则,可见。
三、MVCC 的读取类型
1. 快照读(Snapshot Read)
- 普通
SELECT
语句(无FOR UPDATE
)。 - 不加锁,通过 MVCC 实现一致性读。
- 读取 事务开始时的快照。
2. 当前读(Current Read)
- 加锁读取:
SELECT ... FOR UPDATE
/LOCK IN SHARE MODE
/UPDATE
/DELETE
。 - 读取最新版本,并对记录加锁。
四、MVCC 与事务隔离级别的关系
READ COMMITTED(读已提交)
- 每次执行 SELECT 都生成新的 Read View。
- 可能出现 不可重复读。
REPEATABLE READ(可重复读)
- 事务启动时生成 Read View,整个事务期间复用。
- 解决不可重复读问题。
- InnoDB 默认隔离级别,结合 Next-Key Lock 可避免幻读。
五、MVCC 的工作流程示例
假设事务隔离级别为 REPEATABLE READ:
事务 A
START TRANSACTION;
SELECT * FROM account WHERE id = 1; -- 创建 Read View
事务 B
START TRANSACTION;
UPDATE account SET balance = 200 WHERE id = 1; -- 修改 DB_TRX_ID,写入 Undo Log
COMMIT;
事务 A 再次读取
SELECT * FROM account WHERE id = 1; -- 仍然读取旧版本(通过 Undo Log)
结果:
- A 看到的是事务开始时的版本。
- B 修改的新值对 A 不可见。
六、MVCC 版本链结构图
最新记录:balance = 200, DB_TRX_ID = 30, DB_ROLL_PTR -> V1
V1:balance = 100, DB_TRX_ID = 20, DB_ROLL_PTR -> NULL
-
事务 A 的 Read View:活跃事务 ID 列表 = [30],min_trx_id = 20,max_trx_id = 31。
-
A 读取时发现:
- 最新版本(trx_id=30)在活跃事务列表中 → 不可见。
- 找 Undo Log V1(trx_id=20)→ 可见。
七、MVCC 的优缺点
优点
- 读操作不加锁,提高并发性能。
- 减少死锁风险。
缺点
- 需要维护 Undo Log,占用空间。
- 长事务可能导致 Undo Log 膨胀,影响性能。
八、面试高频问答
Q1:InnoDB 的 MVCC 是如何实现的?
- 通过 隐藏列(DB_TRX_ID、DB_ROLL_PTR)+ Undo Log + Read View。
- 使用版本链实现多版本控制。
Q2:快照读和当前读的区别?
- 快照读:普通 SELECT,使用 MVCC,不加锁。
- 当前读:UPDATE、DELETE、SELECT … FOR UPDATE,读取最新版本并加锁。
Q3:不同隔离级别下 Read View 什么时候创建?
- READ COMMITTED:每次 SELECT 都新建。
- REPEATABLE READ:事务第一次 SELECT 时创建并复用。
Q4:MVCC 如何解决不可重复读?
- 在 REPEATABLE READ 下,事务使用同一个 Read View,始终看到一致的版本。
Q5:幻读能通过 MVCC 解决吗?
- 不能完全解决,需要结合 Next-Key Lock。
九、总结
组件 | 作用 |
---|---|
隐藏列 | 存储事务 ID 和 Undo Log 指针 |
Undo Log | 保存旧版本数据,用于回滚和一致性读 |
Read View | 定义事务可见版本,保证隔离性 |
关键点:
- MVCC 提供 高性能一致性读,减少锁竞争。
- 通过版本链 + Read View 实现事务隔离。
- Undo Log 膨胀问题需要控制长事务。