undo log版本链是行记录通过db_roll_ptr隐式串联形成的逻辑单向链表,而非磁盘上的日志文件链;其本质是最新数据行指向undo log中历史版本的指针链,配合readview按事务可见性规则确定可读版本。

Undo Log版本链不是“日志文件链”,而是行记录里的指针链
很多人一看到“版本链”就去翻 undo_001 文件,结果什么也看不到——因为版本链根本不在磁盘文件里显式组织,它是由每行数据的 DB_ROLL_PTR 隐式串联起来的单向链表。
每次 UPDATE 或 INSERT,InnoDB 会:
- 把旧值写进 Undo Log(物理落盘到 undo 表空间)
- 在当前行记录中更新
DB_TRX_ID为当前事务ID - 把
DB_ROLL_PTR指向刚写入的那条 Undo Log 记录(即上一个版本)
所以“链”是逻辑结构:最新行 → Undo Log 中的历史版本1 → 更早的版本2 → … → 初始插入版本。查 SELECT 时,InnoDB 不是遍历文件,而是顺着 DB_ROLL_PTR 一层层跳转,直到找到对当前事务可见的版本。
ReadView 决定“该停在哪一层”,不是所有版本都可读
版本链只是数据容器,真正控制你能看到哪个版本的,是事务启动瞬间生成的 ReadView。它包含四个关键字段:m_ids(活跃事务ID列表)、min_trx_id、max_trx_id、creator_trx_id。
判断某行版本是否可见的规则很简单:
- 若该版本的
DB_TRX_ID==creator_trx_id:就是本事务改的,可见 - 若
DB_TRX_IDmin_trx_id:修改它的事务早已提交,可见 - 若
DB_TRX_ID∈m_ids:那个事务还没提交,不可见,继续沿DB_ROLL_PTR往上找 - 若
DB_TRX_ID≥max_trx_id:修改发生在本事务启动之后,不可见
这个过程完全在内存中完成,不碰磁盘——这也是快照读快的核心原因。
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
RC 和 RR 隔离级别下,ReadView 创建时机不同,导致链遍历行为差异
READ COMMITTED 每次 SELECT 都新建 ReadView,所以两次查询可能看到不同版本(不可重复读);而 REPEATABLE READ 只在事务第一次 SELECT 时创建 ReadView,后续复用,因此能保证同一事务内多次读一致。
这意味着:
- RR 下,即使其他事务已提交新版本,你的事务仍会沿着版本链“卡”在第一次读到的那个快照位置
- RC 下,每次查询都重新评估活跃事务,版本链可能被“截断”得更短(因为更多事务已提交)
- 无论哪种级别,
UPDATE/SELECT ... FOR UPDATE都不走这条链,而是直接查最新行并加锁(当前读)
Undo Log 不清理,版本链就不断变长,但实际影响取决于事务存活时间
Undo Log 的清理不是按“天”或“大小”自动触发的,而是由最老的活跃 ReadView 决定:只要还有事务依赖某个旧版本(比如一个长事务没结束),对应 Undo Log 就不能删。
常见误判点:
- “大事务”不等于“长事务”:一个批量
UPDATE即使执行10分钟,只要提交了,它的ReadView就消失,不影响 Undo 清理 - 真正危险的是“空闲长事务”:比如应用层开启事务后忘了
COMMIT,它持有的ReadView会让整个版本链冻结 -
innodb_purge_threads负责异步清理,但无法绕过活跃事务约束
所以排查版本链膨胀,第一反应不该是调大 innodb_undo_log_truncate,而是用 SELECT * FROM information_schema.INNODB_TRX 找出未提交的古老事务。










