引言:Undo Log 的核心地位
在 MySQL InnoDB 存储引擎中,undo log(回滚日志) 是支撑事务原子性(Atomicity)和多版本并发控制(MVCC, Multi-Version Concurrency Control)的关键基础设施。
尽管其名称包含"回滚",但 undo log 的作用远不止于事务回滚------它更是实现非阻塞读、快照读、一致性视图(Consistent Read View)的核心机制。
简言之:
- 事务回滚 :当事务显式执行
ROLLBACK或因异常终止时,InnoDB 利用 undo log 将数据页恢复至事务开始前的状态。 - MVCC 支持 :当一个事务执行
SELECT时,若目标行已被其他未提交事务修改,InnoDB 会通过 undo log 构建该行的历史版本,从而提供一致性读,避免读写冲突。
一、Undo Log 的内部结构与存储格式
1.1 Undo Record 的逻辑结构
每个 undo log 条目(undo record)记录了一次数据修改的"反向操作"。例如,对某行执行 UPDATE col = 'new' WHERE id = 10,其对应的 undo record 会保存:
- 主键值(用于定位行)
- 被修改列的旧值
- 事务 ID(trx_id)
- 回滚指针(roll_ptr),指向更早的 undo record(形成版本链)
注 :InnoDB 中每行记录头包含一个 7 字节的
roll_ptr,指向该行最新 undo record。
1.2 Undo Log Segment 与 Rollback Segment
- Rollback Segment(回滚段):InnoDB 使用回滚段管理 undo log。每个回滚段可容纳最多 1023 个 undo log segment(MySQL 8.0 起支持多个回滚段)。
- Undo Log Segment:每个活跃事务(或只读事务首次修改时)分配一个 undo log segment,用于存储其所有 undo records。
- 物理存储 :undo log 存储在 undo tablespace 中(自 MySQL 5.7 起支持独立 undo 表空间,默认启用;8.0 进一步优化)。
1.3 Undo Log 类型
InnoDB 区分两种 undo log:
- INSERT_UNDO:仅用于事务回滚,提交后即可立即释放。
- UPDATE_UNDO:既用于回滚,也用于 MVCC,需等到不再被任何活跃读视图引用后,方可由 purge 线程清理。
二、Undo Log 的生命周期管理
2.1 生成阶段
- 当事务首次修改数据(INSERT/UPDATE/DELETE)时,InnoDB 在内存中的 undo log buffer 中生成 undo record。
- 根据配置(如
innodb_undo_log_truncate、innodb_undo_logs),undo log 被写入对应的 undo tablespace。
2.2 写入与持久化
- Undo log 本身不强制刷盘(与 redo log 不同),但其修改操作会通过 redo log 保护,确保崩溃恢复时能重建 undo log。
- 即:undo log 的持久性依赖于 redo log 的 WAL(Write-Ahead Logging)机制。
2.3 清理阶段(Purge)
- 事务提交后,其 UPDATE_UNDO 并不会立即删除。
- Purge 线程定期扫描已提交事务的 undo log,判断其是否仍被任何活跃读视图(Read View)引用。
- 若无引用,则标记为可回收,并最终从 undo tablespace 中物理删除。
- 自 MySQL 8.0 起,支持自动 truncate undo tablespace(通过
innodb_undo_log_truncate=ON),防止空间无限增长。
三、关键组件关系图解
3.1 Undo Log 写入与清理流程

3.2 Undo Log vs Redo Log 对比表
| 维度 | Undo Log | Redo Log |
|---|---|---|
| 主要目的 | 支持事务回滚 + MVCC | 崩溃恢复(保证持久性) |
| 内容 | 逻辑反向操作(旧值) | 物理/逻辑正向操作(新值) |
| 持久性要求 | 非强制刷盘(由 redo 保护) | 必须刷盘(WAL 机制) |
| 存储位置 | Undo Tablespace(ibundo 文件) | Redo Log Files(ib_logfile*) |
| 生命周期 | 事务提交后可能长期存在(MVCC) | checkpoint 后可覆盖 |
| 线程管理 | Purge 线程清理 | Log Writer / Flush 线程管理 |
| 是否用户可见 | 否(内部机制) | 否 |
四、MySQL 5.7 与 8.0 在 Undo Log 管理上的关键演进
| 特性 | MySQL 5.7 | MySQL 8.0 |
|---|---|---|
| Undo 表空间 | 支持独立 undo 表空间(需手动启用) | 默认启用独立 undo 表空间(至少 2 个) |
| Undo 截断 | 支持 innodb_undo_log_truncate |
更高效截断,支持在线收缩 |
| 回滚段数量 | 最多 128 个(受 innodb_rollback_segments 限制) |
最多 127 个回滚段 × 每段 1023 slots ≈ 13 万并发事务 |
| 临时表 undo | 与普通 undo 共享 | 引入独立的 临时 undo log,提升性能 |
| DDL 原子性支持 | 有限 | 利用 undo log 实现原子 DDL(如 DROP TABLE 可回滚) |
注:MySQL 8.0 将 undo log 管理彻底模块化,显著提升了高并发下的扩展性和空间回收效率。
五、高频面试题
Q1:Undo log 和 redo log 的区别是什么?
答:
- Undo 用于回滚和 MVCC,记录"旧值";Redo 用于崩溃恢复,记录"新值"。
- Undo 存于 undo tablespace,Redo 存于 redo log files。
- Undo 由 purge 线程清理,Redo 由 checkpoint 机制覆盖。
Q2:事务提交后,undo log 会立即删除吗?
答:
- INSERT_UNDO 会立即标记为可回收;
- UPDATE_UNDO 需等待所有活跃 Read View 不再引用其版本后,才由 purge 线程删除。
Q3:如何避免 undo 表空间无限增长?
答:
- 启用
innodb_undo_log_truncate=ON(默认 ON); - 设置合理的
innodb_purge_rseg_truncate_frequency; - 监控长事务(
information_schema.innodb_trx),因其会阻塞 purge。
Q4:Undo log 如何支持 MVCC?
答 :
当查询需要一致性读时,InnoDB 通过行的 roll_ptr 遍历 undo 链,找到符合当前 Read View 的历史版本,从而构建快照。
Q5:MySQL 8.0 为何将 undo log 独立成表空间?
答:
- 避免 undo 与系统表空间(ibdata1)耦合,便于管理与扩容;
- 支持在线 truncate,解决空间膨胀问题;
- 提升并发事务处理能力(更多回滚段)。