MySQL 事务隔离的底层实现原理
MVCC是通过 Read View(读视图) 和 undo 日志 的协同配合,实现了事务隔离级别的核心功能(尤其是读已提交和可重复读)。两者的分工和协作关系可以总结为:
1. undo 日志:提供“历史版本”的物理基础
- undo 日志记录了数据被修改前的旧值,每次事务对数据执行 INSERT/UPDATE/DELETE 时,InnoDB 会将修改前的版本写入 undo 日志,并通过行记录的
DB_ROLL_PTR指针串联起来,形成一条版本链(每个版本包含对应修改的事务 IDDB_TRX_ID)。 - 例如:一行数据被事务100、事务200先后修改,undo 日志会形成类似
当前版本 → 事务200修改前的版本 → 事务100修改前的版本 → 初始版本的链式结构。 - 作用:为 MVCC 提供了“可追溯的历史数据”,是 Read View 能够读取到旧版本的物质基础。
2. Read View:决定“哪个版本可见”的逻辑规则
- Read View 是事务在启动(或查询时)生成的一个快照,包含当前活跃事务的 ID 列表、最小活跃事务 ID(min_trx_id)、最大事务 ID(max_trx_id)等信息。
- 它的核心作用是判断版本链中的某个版本是否对当前事务可见:
- 若版本的
DB_TRX_ID(修改该版本的事务 ID)小于 min_trx_id:说明该版本是事务启动前已提交的,可见。 - 若版本的
DB_TRX_ID大于等于 max_trx_id:说明该版本是事务启动后新生成的,不可见,需找更旧的版本。 - 若
DB_TRX_ID在 [min_trx_id, max_trx_id) 之间:需检查该事务 ID 是否在活跃列表中,若在(未提交)则不可见,否则(已提交)可见。
- 若版本的
- 作用:提供了“可见性判断规则”,让事务能从版本链中筛选出符合当前隔离级别的数据版本。
3. 两者协同:实现隔离级别的核心逻辑
- 没有 undo 日志:Read View 就没有可筛选的历史版本,无法实现“读旧数据”,只能读取最新数据(类似读未提交)。
- 没有 Read View:undo 日志的版本链就无法被正确筛选,事务可能读取到未提交的脏数据,或无法保证重复读。
两者的配合流程如下:
- 事务需要读取数据时,先通过 Read View 确定可见性规则。
- 从当前数据版本开始,沿着 undo 日志的版本链依次回溯,用 Read View 规则判断每个版本是否可见。
- 找到第一个符合规则的版本,作为该事务的读取结果。
4. 对隔离级别的影响
- 读已提交(RC):每次查询都会生成新的 Read View,因此能读取到查询时已提交的最新版本(避免脏读,但允许不可重复读)。
- 可重复读(RR,MySQL 默认):事务启动时生成一次 Read View,整个事务期间复用,因此多次查询会读到相同版本(保证重复读,避免不可重复读)。
这两种隔离级别的行为差异,本质上就是Read View 的生成时机不同,而 undo 日志的版本链则为这种差异提供了支持。
总结
- undo 日志是“历史版本的存储载体”,提供了可追溯的数据源;
- Read View 是“可见性的判断标准”,决定了事务能看到哪个版本;
- 两者协同,使得 MVCC 能够在不加锁的情况下,实现不同隔离级别对“读写不阻塞”和“数据一致性”的要求。

浙公网安备 33010602011771号