MySQL 8.0Notes

第 18 章:Undo 与事务版本链

zjc 于 2026-01-18 发布

这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Undo log 记录行的旧版本,用于事务回滚和 MVCC 一致性读。旧版本通过版本链关联,长时间活跃事务会阻止 purge 清理。

18.1 事务 ID

BEGIN
  -> first read snapshot
  -> modifying statement
     -> assign trx_id / roll_ptr
        -> write undo
           -> modify row

聚簇索引记录中的隐藏字段:

字段 含义
DB_TRX_ID 最近修改事务
DB_ROLL_PTR 指向 undo 记录
hidden row id 无主键表使用

18.2 Undo 类型

类型 场景
insert undo 插入回滚
update undo 更新或删除回滚与 MVCC

INSERT 旧版本对一致性读不可见,回滚后可较快清理;UPDATE/DELETE undo 需要服务快照读取。

18.3 回滚

ROLLBACK
  -> scan undo records
     -> restore old values
        -> undo secondary index changes
           -> write redo for rollback
              -> finish trx

大事务回滚耗时与修改量相关,可能持续产生 IO 和锁等待。

18.4 MVCC

一致性读根据 read view 判断版本可见性:

current record
  -> visible?
     |-- yes: return
     +-- no: follow DB_ROLL_PTR
        -> old version
           -> visible?

Read View 记录活跃事务集合:

  1. m_ids;
  2. min_trx_id;
  3. max_trx_id;
  4. creator_trx_id。

可见性判断核心:

条件 可见性
trx_id == creator 自己修改可见
trx_id < min 已提交早于快照,可见
trx_id >= max 快照后开启,不可见
在活跃列表 快照时未提交,不可见

18.5 RR 与 RC

隔离级别 read view
REPEATABLE READ 事务内一致性读快照通常较稳定
READ COMMITTED 每条一致性读语句创建新快照

锁读仍按锁规则执行,REPEATABLE READ 不等价于完全串行化。

18.6 长事务

长事务持有旧 read view:

long transaction active
  -> old versions needed
     -> purge cannot advance
        -> history list grows
           -> undo tablespace grows

排查:

SELECT trx_id, trx_state, trx_started, trx_rows_modified
FROM information_schema.innodb_trx
ORDER BY trx_started;

SHOW ENGINE INNODB STATUS;

18.7 Undo 表空间

8.0 支持独立 undo 表空间管理:

SELECT * FROM information_schema.innodb_tablespaces
WHERE SPACE_TYPE='Undo';

可执行 truncate,但前提是 purge 能推进且无活跃事务依赖旧版本。

18.8 源码入口

文件 职责
trx0trx.cc 事务生命周期
trx0rseg.cc 回滚段
row0undo.cc 行回滚
row0vers.cc 版本可见性
read0read.cc Read View

断点:

b ReadView::changes_visible
b trx_assign_read_view
b row_vers_build_for_consistent_read

本章小结

Undo 同时服务回滚和 MVCC。当前记录通过 roll pointer 连接历史版本,read view 决定可见性。长事务会延长版本链并阻塞 purge。

思考题

  1. DB_ROLL_PTR 的作用是什么?
  2. INSERT undo 和 UPDATE undo 有什么差异?
  3. read view 如何判断版本可见?
  4. 长事务为什么导致 undo 膨胀?
  5. RC 和 RR 的 read view 创建时机有什么不同?