前置知识: MySQL

MVCC原理

1 minAdvanced2026/6/14

MySQL InnoDB MVCC原理:隐藏列、Read View、undo log版本链与可见性判断算法

1. MVCC 基础组件

1.1 隐藏列

InnoDB 每行数据包含三个隐藏列:

列名大小用途
DB_TRX_ID6字节最后修改该行的事务ID
DB_ROLL_PTR7字节回滚指针,指向 undo log 前版本
DB_ROW_ID6字节隐藏自增ID(无主键时使用)

1.2 Undo Log 版本链

当前行:{data='Alice', trx_id=300, roll_ptr → undo_2}

undo_2:{data='Bob', trx_id=200, roll_ptr → undo_1}

undo_1:{data='Charlie', trx_id=100, roll_ptr → NULL}

每次 UPDATE 都在 undo log 中保留旧版本,形成版本链。

2. Read View

2.1 创建时机

隔离级别创建时机
READ COMMITTED每次 SELECT 创建新 Read View
REPEATABLE READ事务首次 SELECT 创建,复用

2.2 核心字段

creator_trx_id:创建该 Read View 的事务ID
m_ids:创建时所有活跃(未提交)事务ID列表
min_trx_id:活跃事务最小ID
max_trx_id:下一个将分配的事务ID

3. 可见性判断

对于版本链中某版本的 trx_id:

if trx_id == creator_trx_id:
    → 可见(自己修改的)

if trx_id < min_trx_id:
    → 可见(事务在 Read View 创建前已提交)

if trx_id >= max_trx_id:
    → 不可见(事务在 Read View 创建后才开始)

if min_trx_id <= trx_id < max_trx_id:
    if trx_id in m_ids:
        → 不可见(事务未提交)
    else:
        → 可见(事务已提交)

4. 版本遍历

1. 读取当前行的 trx_id
2. 判断当前版本是否可见
3. 可见 → 返回该版本数据
4. 不可见 → 沿 roll_ptr 找到上一个版本
5. 重复步骤2-4
6. 版本链遍历完仍不可见 → 该行对当前事务不可见

5. 快照读 vs 当前读

-- 快照读:使用 MVCC
SELECT * FROM t WHERE id = 1;

-- 当前读:读取最新数据 + 加锁
SELECT * FROM t WHERE id = 1 FOR UPDATE;
SELECT * FROM t WHERE id = 1 LOCK IN SHARE MODE;
UPDATE t SET col = val WHERE id = 1;
DELETE FROM t WHERE id = 1;

6. Purge 机制

Purge 线程负责清理不再需要的 undo log:
1. 检查某版本是否对所有活跃事务都不可见
2. 如果是,该版本可以安全清理
3. 长事务会阻止 Purge,导致 undo log 膨胀