InnoDB存储引擎对MVCC的实现¶
MVCC (Multi-Version Concurrency Control) 的核心目的是为了实现“读写不冲突”。在不加锁的情况下,让读操作读取历史版本数据(快照读),而写操作修改最新版本数据。
MVCC 会定期进行版本的回收,以防止数据库中的版本无限增长。
- MVCC 通过创建数据的多个版本和使用快照读取来实现并发控制。
- 读操作使用旧版本数据的快照,写操作创建新版本,并确保原始版本仍然可用。
- 这样,不同的事务可以在一定程度上并发执行,而不会相互干扰,从而提高了数据库的并发性能和数据一致性。
InnoDB 对 MVCC 的实现主要依赖于三个“隐式”组件:
- 隐藏字段 (Hidden Columns)
- Undo Log (回滚日志)
- ReadView (读视图)
简单来说:Undo Log 保存了历史快照,隐藏字段将这些快照串联成链,而 ReadView 则充当裁判,决定当前事务能看到链条上的哪一个版本。
一、 实现机制详解 (The How)¶
- 隐藏字段 (Hidden Columns)
- InnoDB 在每行数据背后都默默维护了几个不可见的字段,最关键的是以下两个 :
- DB_TRX_ID (6字节):最近一次修改(Insert/Update)该行数据的事务ID。
- DB_ROLL_PTR (7字节):回滚指针。它指向 Undo Log 中该行的上一个版本。
- Undo Log (版本链)
- 当事务修改数据时,InnoDB 不会直接覆盖旧数据,而是先将旧数据拷贝到 Undo Log 中。
- 通过 DB_ROLL_PTR 指针,最新的数据行可以像链表一样,顺藤摸瓜找到之前的所有老版本。这就是版本链 。
- ReadView (读视图/可见性判断)
- ReadView 是事务进行快照读 (Snapshot Read) 时产生的“读视图”。它记录了生成时刻系统活跃事务的状态,用于判断版本链中的哪个版本对当前事务可见 。
ReadView 包含四个核心字段(变量名对应源码):
- m_creator_trx_id:生成该 ReadView 的事务ID(即“我”的ID)。
- m_ids:生成 ReadView 时,系统中当前活跃(未提交)的事务ID列表。
- m_up_limit_id (min_trx_id):m_ids 中最小的事务ID。如果数据版本的事务ID小于它,说明那是很久以前提交的,肯定可见。
- m_low_limit_id (max_trx_id):生成 ReadView 时,系统应该分配给下一个事务的ID(即最大事务ID + 1)。
二、 可见性算法 (The Why)¶
当你的事务读取一行数据时,InnoDB 会拿到该行数据的 DB_TRX_ID,拿着它去跟你的 ReadView 做比较。规则如下 :
| 比较规则 | 解释 | 结果 |
|---|---|---|
trx_id == creator_id |
数据就是我自己改的 | 可见 |
trx_id < min_trx_id |
数据在快照创建前已提交 | 可见 |
trx_id >= max_trx_id |
数据是快照创建后才开启的事务改的 | 不可见 |
trx_id IN m_ids |
数据是由此时还在活跃(未提交)的事务改的 | 不可见 |
trx_id NOT IN m_ids |
虽在区间内,但不在活跃列表,说明已提交 | 可见 |
如果当前版本不可见,InnoDB 就会顺着 DB_ROLL_PTR 找到 Undo Log 中的下一个版本,重复上述判断,直到找到可见版本或链表结束。
三、 RC 与 RR 的区别¶
Read Committed (RC) 和 Repeatable Read (RR) 隔离级别的核心区别,仅仅在于生成 ReadView 的时机不同 。
- RC (读已提交):每次执行 SELECT 语句时,都会重新生成一个新的 ReadView。
- 结果:能读到其他事务刚刚提交的数据。
- RR (可重复读):第一次执行 SELECT 语句时生成 ReadView,后续所有的 SELECT 都复用这个 ReadView。
- 结果:不管其他事务怎么提交,我看到的永远是第一次读取时的快照(实现了可重复读)。
四、总结¶
- MVCC 利用 Undo Log 构建数据的历史版本,利用 ReadView 决定当前事务能看到哪个版本。
- 它主要解决了读写并发问题,让查询不用加锁,极大提高了数据库的并发性能。
- 当前读 (Current Read):注意,MVCC 只作用于普通 SELECT。如果你使用 SELECT ... FOR UPDATE 或 UPDATE/DELETE,那是当前读,会读取最新版本并加锁(Record Lock 或 Next-Key Lock),那就不走 MVCC 这一套快照逻辑了 。