MySQL 事务隔离的底层实现原理

MVCC是通过 Read View(读视图)undo 日志 的协同配合,实现了事务隔离级别的核心功能(尤其是读已提交和可重复读)。两者的分工和协作关系可以总结为:

1. undo 日志:提供“历史版本”的物理基础

  • undo 日志记录了数据被修改前的旧值,每次事务对数据执行 INSERT/UPDATE/DELETE 时,InnoDB 会将修改前的版本写入 undo 日志,并通过行记录的 DB_ROLL_PTR 指针串联起来,形成一条版本链(每个版本包含对应修改的事务 ID DB_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 日志的版本链就无法被正确筛选,事务可能读取到未提交的脏数据,或无法保证重复读。

两者的配合流程如下:

  1. 事务需要读取数据时,先通过 Read View 确定可见性规则。
  2. 从当前数据版本开始,沿着 undo 日志的版本链依次回溯,用 Read View 规则判断每个版本是否可见。
  3. 找到第一个符合规则的版本,作为该事务的读取结果。

4. 对隔离级别的影响

  • 读已提交(RC):每次查询都会生成新的 Read View,因此能读取到查询时已提交的最新版本(避免脏读,但允许不可重复读)。
  • 可重复读(RR,MySQL 默认):事务启动时生成一次 Read View,整个事务期间复用,因此多次查询会读到相同版本(保证重复读,避免不可重复读)。

这两种隔离级别的行为差异,本质上就是Read View 的生成时机不同,而 undo 日志的版本链则为这种差异提供了支持。

总结

  • undo 日志是“历史版本的存储载体”,提供了可追溯的数据源;
  • Read View 是“可见性的判断标准”,决定了事务能看到哪个版本;
  • 两者协同,使得 MVCC 能够在不加锁的情况下,实现不同隔离级别对“读写不阻塞”和“数据一致性”的要求。
posted @ 2025-07-04 13:46  认真的刻刀  阅读(44)  评论(0)    收藏  举报