MVCC (Multiversion Concurrency Control,多版本并发控制) 是 InnoDB 存储引擎为了实现高并发读写而设计的核心机制。它旨在让“写操作不阻塞读操作,读操作也不阻塞写操作”,从而有效提升数据库的并发处理能力。
MVCC 是什么?
简单来说,MVCC 通过维护数据行的多个历史版本,使得读操作(快照读)可以访问到某个一致性快照,而不必读取最新的、可能正在被修改的数据。这主要用于普通的 SELECT 语句(即快照读),例如:
SELECT * FROM user WHERE id = 1;
而对于 SELECT ... FOR UPDATE、UPDATE、DELETE 等当前读操作,则需要通过加锁来保证数据一致性,不适用 MVCC 机制。
MVCC 的三大核心支柱
MVCC 的实现依赖于三个关键组件:
1. Undo Log(历史版本链)
每当执行 UPDATE 或 DELETE 操作时,InnoDB 不仅会将新数据写入当前行,还会将旧数据拷贝到 Undo Log 中。每行数据都有一个隐藏的 roll_pointer 指针,指向其在 Undo Log 中的上一个版本,从而形成一条从最新版本指向历史版本的单向链表:
最新版本 ← roll_pointer — 上一版本 ← roll_pointer — 更早版本...
这条版本链是 MVCC 能够回溯历史数据的“时光机”。
2. 隐藏字段(trx_id 与 roll_pointer)
InnoDB 为每行数据隐式地添加了两个重要的字段:
trx_id:最近一次修改该行数据的事务ID。
roll_pointer:指向该行上一个版本数据在 Undo Log 中位置的指针。
这两个字段是 MVCC 实现版本追踪的基础。
3. Read View(可见性规则)
当事务执行快照读时,InnoDB 会为该事务生成一个 Read View(读视图),它是一个快照时刻的系统状态,主要包含:
m_ids:生成 Read View 时,系统中活跃(未提交)的事务ID列表。
min_trx_id:m_ids 中的最小值。
max_trx_id:生成 Read View 时,系统已分配的下一个事务ID(即当前最大事务ID+1)。
可见性判断规则:根据当前行的 trx_id 与 Read View 中的信息进行判断,以决定该版本对当前事务是否可见。如果不可见,则通过 roll_pointer 沿 Undo Log 版本链找到上一个版本,并重复此判断过程,直到找到一个可见的数据版本。
MVCC 如何实现“快照读”?
假设一行数据存在版本链:V3(trx_id=30) → V2(trx_id=20) → V1(trx_id=10)。
事务A(trx_id=25)发起快照读,其 Read View 中记录活跃事务可能包含30。
- 从最新的 V3 开始判断,发现其
trx_id=30 大于等于 Read View 的 max_trx_id(或存在于m_ids中),对事务A不可见。
- 通过
roll_pointer 找到上一个版本 V2,其 trx_id=20 小于 Read View 的 min_trx_id,对事务A可见。
- 因此,事务A读取到的是 V2 版本的数据,而非最新的 V3。这就实现了在事务开始时的一致性读取。
MVCC 在不同隔离级别下的行为差异
MVCC 主要在读已提交(RC)和可重复读(RR)这两种隔离级别下发挥作用,但其行为有所不同,这也是理解数据库事务隔离级别的关键。
- 读已提交 (RC):每次执行快照读时,都会生成一个新的 Read View。因此,每次都能看到最新已提交的数据,这会导致“不可重复读”现象。
- 可重复读 (RR):在同一个事务内,只有第一次执行快照读时会生成一个 Read View,后续所有的快照读都复用这个视图。因此,在整个事务期间,看到的数据快照是一致的,从而避免了不可重复读。
注意:RR 级别下,MVCC 本身无法完全避免幻读。幻读的解决主要依赖于 InnoDB 的 Next-Key Lock(临键锁)机制。MVCC 负责快照读的一致性,而锁负责当前读和写操作的一致性。
MVCC 与锁的关系
| 这是一个常见的理解难点,两者分工明确: |
操作类型 |
是否加锁 |
是否使用 MVCC |
说明 |
普通 SELECT |
否 |
是 |
快照读,依赖 MVCC 实现非阻塞读取。 |
SELECT ... FOR UPDATE |
是 |
否 |
当前读,需要加锁(记录锁、间隙锁等)。 |
UPDATE / DELETE / INSERT |
是 |
否 |
写操作属于当前读,必须加锁以保证数据正确性。 |
核心结论:MVCC 是一种乐观并发控制思想在读操作上的体现,它优化了读并发;但对于写操作,InnoDB 仍然使用锁这种悲观并发控制机制来保证安全。它们是相辅相成的关系。
MVCC 的实用价值与总结
- 提升读并发:读操作无需加锁,极大降低了读写事务之间的阻塞,支撑了高并发的 OLTP 场景。
- 高效实现事务隔离:为 RC 和 RR 隔离级别提供了高效、无锁的读一致性实现方案。
- 一材多用:Undo Log 不仅服务于 MVCC,还用于事务回滚和系统崩溃恢复,设计非常精巧。
总结:InnoDB 的 MVCC 机制,通过 Undo Log 构建数据版本链,利用隐藏字段 trx_id 和 roll_pointer 进行版本追踪,并基于 Read View 的规则来决定数据的可见性。它在不同的隔离级别下通过改变 Read View 的生成策略(RC每次生成,RR首次生成),来平衡“一致性”与“并发度”。理解 MVCC 是深入掌握 MySQL InnoDB 存储引擎并发原理和数据库系统设计思想的基石。
|