Read View 决定可见性

Read View 决定可见性

概述

Read View(读视图)是 InnoDB MVCC 机制中的核心数据结构,它决定了在当前事务中,哪些数据版本是”可见的”。每次事务读取数据时,InnoDB 都会通过 Read View 来判断:这个版本能看,还是不能看。

Read View 是什么

Read View 是事务在某个时间点对数据库的”快照”,它记录了该时刻系统中所有活跃事务的信息。

Read View 数据结构
│
├── m_creator_trx_id: 创建该 Read View 的事务 ID
├── m_low_limit_id:   下一个将被分配的事务 ID(上限)
├── m_up_limit_id:    当前活跃事务中最小的 ID(下限)
└── m_ids:            创建时刻所有活跃事务的 ID 列表(有序数组)

字段详解

字段 含义 举例
m_creator_trx_id 当前事务的 ID 25
m_low_limit_id 下一个将被分配的 ID 30(说明有 29 个子事务)
m_up_limit_id 活跃事务中最小的 ID 26
m_ids 快照时刻所有活跃事务 ID 列表 [26, 28, 29]

可见性判断规则

当 InnoDB 需要判断某行记录的当前版本(其 DB_TRX_ID = T)是否可见时,遵循以下规则:

规则一:当前事务自己的修改,总是可见

IF DB_TRX_ID == m_creator_trx_id
    → ✅ 可见(自己改的,不看自己看谁)

规则二:比上界之后的事务,不可见

IF DB_TRX_ID >= m_low_limit_id
    → ❌ 不可见(这个事务在快照之后才启动,它的修改看不到)

规则三:比下界之前的事务,总是可见

IF DB_TRX_ID < m_up_limit_id
    → ✅ 可见(这个事务在快照时已经提交了,应该看到)

规则四:在上下界之间,检查活跃事务列表

IF m_up_limit_id <= DB_TRX_ID < m_low_limit_id
    → 检查 m_ids 列表中是否包含 DB_TRX_ID
        ├── 包含 → ❌ 不可见(该事务在快照时刻还活跃着)
        └── 不包含 → ✅ 可见(该事务在快照前已提交)

流程图

               DB_TRX_ID
                   │
                   ▼
         ┌─────────────────────┐
         │ == m_creator_trx_id │ → ✅ 可见
         └─────────────────────┘
                   │
                   ▼
         ┌─────────────────────┐
         │ < m_up_limit_id     │ → ✅ 可见
         └─────────────────────┘
                   │
                   ▼
         ┌─────────────────────┐
         │ >= m_low_limit_id   │ → ❌ 不可见
         └─────────────────────┘
                   │
            (在中间范围)
                   │
                   ▼
         ┌─────────────────────┐
         │ 在 m_ids 列表中?    │
         ├── 是 → ❌ 不可见    │
         └── 否 → ✅ 可见      │

完整示例

场景设定

事务 ID 假设:
事务 T1: ID=10(已提交)
事务 T2: ID=20(已提交)
事务 T3: ID=25(当前事务)
事务 T4: ID=30(活跃)
事务 T5: ID=35(活跃)

当前事务 T3 的 Read View

m_creator_trx_id = 25
m_low_limit_id   = 36(下一个即将分配的事务 ID)
m_up_limit_id    = 30(活跃事务中最小的)
m_ids            = [30, 35]

可见性判断

-- 读取 id=1 的行,该行 DB_TRX_ID = 10
-- 10 < m_up_limit_id(30) → ✅ 可见(事务 10 已经提交了)

-- 读取 id=2 的行,该行 DB_TRX_ID = 20
-- 20 < m_up_limit_id(30) → ✅ 可见(事务 20 也提交了)

-- 读取 id=3 的行,该行 DB_TRX_ID = 25
-- 25 == m_creator_trx_id → ✅ 可见(自己改的)

-- 读取 id=4 的行,该行 DB_TRX_ID = 30
-- 30 在 m_ids 列表中 → ❌ 不可见,需要回溯版本链

-- 读取 id=5 的行,该行 DB_TRX_ID = 40
-- 40 >= m_low_limit_id(36) → ❌ 不可见,需要回溯版本链

Read View 的创建时机

REPEATABLE READ(MySQL 默认)

-- 事务中第一次 SELECT 时创建 Read View
BEGIN;
-- 还没有 Read View
SELECT * FROM user WHERE id = 1;  -- → 创建 Read View A
SELECT * FROM user WHERE id = 2;  -- → 复用 Read View A
SELECT * FROM user WHERE id = 3;  -- → 复用 Read View A
COMMIT;
-- 造成的结果:整个事务看到的是同一时刻的快照

READ COMMITTED

BEGIN;
SELECT * FROM user WHERE id = 1;  -- → 创建 Read View A
-- 其他事务提交了修改
SELECT * FROM user WHERE id = 1;  -- → 创建 Read View B
-- 同一行两次读取结果可能不同
COMMIT;

Read View 的释放

Read View 在事务提交或回滚时被释放:

  • Read View = 快照 = 指向 Undo Log 中某个版本的信息
  • Read View 未释放 → 那些版本不能被 Purge 线程清理
  • 长事务阻止 Read View 释放,导致 Undo Log 膨胀

面试要点

  1. Read View 的本质:事务快照时刻的活跃事务"名单"
  2. 可见性四规则:自己可见 → 已提交可见 → 未开始不可见 → 快照时活跃的不可见
  3. 与隔离级别的关系:REPEATABLE READ 复用 Read View,READ COMMITTED 重建 Read View
  4. 面试高频:"Read View 如何决定可见性?"——把四个规则讲清楚即可
  5. 长事务的代价:Read View 不释放 → 版本无法清理 → Undo 膨胀
© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容