MVCC(多版本并发控制)的底层实现核心在于数据版本链 和可见性判断规则(Read View) ,InnoDB 通过 Undo Log
和 Read View
协同工作实现这一机制。以下是详细原理:
一、核心组件与流程
1. 数据版本链 (Undo Log
)
- 作用:存储数据行的历史版本,构成版本链。
- 结构 :
- 每行数据包含两个隐藏字段:
DB_TRX_ID
(6字节):最近修改该行的事务ID。DB_ROLL_PTR
(7字节):指向该行上一个历史版本在Undo Log
中的指针。
- 每次更新数据时:
- 生成新的
Undo Log
记录旧数据。 - 新行写入当前数据,
DB_TRX_ID
设为当前事务ID。 DB_ROLL_PTR
指向刚生成的Undo Log
记录。
- 生成新的
- 每行数据包含两个隐藏字段:
- 版本链形成 :通过
DB_ROLL_PTR
指针,将同一数据行的所有历史版本串联成一个链表(按修改时间倒序),链头是最新版本。
2. 快照视图 (Read View
)
- 作用 :事务执行快照读 (如
SELECT
)时创建,用于判断版本链中哪个版本对该事务可见。 - 关键属性 :
m_ids
:生成Read View
时,系统中所有活跃(未提交)事务ID的集合。min_trx_id
:m_ids
中的最小事务ID。max_trx_id
:生成Read View
时,系统应分配给下一个新事务的ID(即当前最大事务ID+1)。creator_trx_id
:创建该Read View
的事务ID(对于只读事务,可能为0)。
- 可见性判断规则 :遍历版本链时,对每个版本的
DB_TRX_ID
(记为 trx_idtrx\_idtrx_id) 应用以下规则:- 可访问 :如果 trx_id<min_trx_idtrx\_id < min\_trx\_idtrx_id<min_trx_id,说明该版本在
Read View
创建前已提交,可见。 - 不可访问 :如果 trx_id≥max_trx_idtrx\_id \geq max\_trx\_idtrx_id≥max_trx_id,说明该版本在
Read View
创建后才生成,不可见。 - 活跃事务 :如果 min_trx_id≤trx_id<max_trx_idmin\_trx\_id \leq trx\_id < max\_trx\_idmin_trx_id≤trx_id<max_trx_id:
- 若 trx_id∈m_idstrx\_id \in m\_idstrx_id∈m_ids,说明该版本由未提交的事务修改,不可见。
- 若 trx_id∉m_idstrx\_id \notin m\_idstrx_id∈/m_ids,说明该版本在
Read View
创建时已提交,可见。
- 自身修改 :如果 trx_id=creator_trx_idtrx\_id = creator\_trx\_idtrx_id=creator_trx_id,说明该版本是当前事务自己修改的,可见。
- 可访问 :如果 trx_id<min_trx_idtrx\_id < min\_trx\_idtrx_id<min_trx_id,说明该版本在
- 遍历过程 :从最新版本开始,依次检查
DB_TRX_ID
,直到找到第一个满足可见性条件的版本。
二、不同隔离级别的实现差异
1. 读已提交 (READ COMMITTED
)
Read View
生成时机 :每次执行快照读时 都会生成一个新的Read View
。- 效果 :每次读都能看到最新已提交的数据。其他事务只要提交,当前事务的下一次读就能看到。
2. 可重复读 (REPEATABLE READ
- InnoDB默认)
Read View
生成时机 :在事务中第一次执行快照读时 生成,后续所有快照读都复用同一个Read View
。- 效果 :在整个事务期间,看到的数据版本是一致的(第一次读时的快照),不受其他已提交事务影响,实现了可重复读 。同时,通过
Next-Key Lock
防止幻读。
三、关键概念区分
- 快照读 (
Snapshot Read
) :- 普通
SELECT
语句(不加FOR UPDATE/SHARE
)。 - 基于 MVCC 机制读取历史版本。
- 无锁(或仅加极轻量级的锁,如检查 schema 版本)。
- 普通
- 当前读 (
Current Read
) :SELECT ... FOR UPDATE/SHARE
,UPDATE
,DELETE
,INSERT
。- 总是读取最新已提交的数据版本。
- 需要加锁(记录锁、间隙锁、Next-Key Lock)以保证数据一致性和防止冲突。
四、垃圾回收 (Purge
)
- 问题 :历史版本 (
Undo Log
) 不会永久保留。 - 机制 :InnoDB 后台的
Purge
线程负责清理不再需要的旧版本数据。 - 清理条件 :当系统中没有 任何
Read View
需要访问某个旧版本时,该版本才能被安全删除。
总结 :MVCC 的底层实现 = Undo Log
构建版本链 + Read View
判断可见性 。通过控制 Read View
的生成时机,实现了不同隔离级别的语义。快照读依赖 MVCC 提供无锁并发读,当前读则依赖锁机制保证数据强一致性。