undo log 不只是“回滚日志”。回滚需要它,MVCC 的历史版本也需要它;记录上的 roll pointer、事务 id 和 undo 版本链一起,决定了一个事务能看到哪一版数据。
这也是 undo 最容易被低估的地方:它让快照读成为可能,也会被长事务拖成系统债务。读懂 undo,才能理解回滚、快照读、历史版本清理和长事务治理为什么是一组问题。
先把机制边界说清楚
undo log 记录的是数据被修改前的旧值或反向操作。事务回滚时,InnoDB 通过 undo 把记录恢复到修改前;快照读时,InnoDB 通过记录上的 roll pointer 沿 undo 版本链找到对当前事务可见的历史版本。
整体路径
上面这张图先看入口和边界:宏观上,更新一行记录时,InnoDB 会先把旧版本写入 undo,再修改聚簇索引记录中的事务 id 和回滚指针。事务提交后,undo 不会立刻删除,因为可能还有旧 ReadView 需要它。后台 purge 线程会在确认没有事务需要旧版本后清理 undo。
第二张图再看结构关系。
底层流程
底层拆解先看数据结构。「undo log」至少涉及下面几类结构:
- undo record:保存旧值或反向操作信息。
- rollback segment:组织 undo 页和事务回滚信息。
- trx_id:记录最后修改该行的事务编号。
- roll_pointer:从聚簇索引记录指向 undo 版本链。
- history list:等待 purge 清理的历史版本集合。
再看完整执行流程:
- 事务更新记录前生成 undo record。
- 聚簇索引记录写入新的 trx_id 和 roll_pointer。
- 事务回滚时沿 undo 执行反向修改。
- 快照读发现当前版本不可见时,沿 undo 链查找旧版本。
- 所有相关 ReadView 结束后,purge 清理历史 undo。
取舍与边界
版本差异上,MySQL 8.0 对 undo 表空间管理、truncate 和监控能力比早期版本更成熟。5.7 也有独立 undo 表空间配置,但治理体验弱一些。无论版本如何,长事务拖住 purge 的本质不会改变。
undo 的短板是它会被长事务拖成系统性债务。一个长时间不提交的快照读,可能让大量历史版本无法清理;更新越频繁,undo 链越长,purge 压力、空间占用和可见性判断成本都会上升。
典型问题:用机制化例子排查
长事务问题可以用报表查询来理解:一个长时间不结束的快照读,会让系统保留更早的历史版本,写入越多,undo 清理压力越大。
可以落到这些动作:
- 监控 history list length、长事务和 undo 表空间增长。
- 报表和导出任务避免在主库开启长快照事务。
- 业务代码不要把事务包到远程调用或人工操作之外。
- 大量更新任务要拆批提交,给 purge 留出持续清理窗口。
收束:历史版本也有成本
undo log 让回滚和快照读成为可能,但它不是免费的历史档案。事务越长,系统越要为过去保留更多版本;长事务治理,本质上也是 undo 成本治理。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 MySQL」,欢迎关注公众号 「十三Tech」。后续会继续按机制、图解和实战排查这条线更新。

