[小技巧71]从回滚到 MVCC:全面解析 MySQL Undo Log 机制

引言: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_truncateinnodb_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,解决空间膨胀问题;
  • 提升并发事务处理能力(更多回滚段)。
相关推荐
知识即是力量ol1 小时前
口语八股:MySQL 核心原理系列(一):索引篇
java·数据库·mysql·八股·索引·面试技巧
funnycoffee1231 小时前
word vba提取所有表格到1个新的文档中
数据库·word
野犬寒鸦1 小时前
缓存与数据库一致性的解决方案:实际项目开发可用
java·服务器·数据库·后端·缓存
倔强的石头1061 小时前
KingbaseES 文档数据实践:MongoDB 兼容性评估与替换落地
数据库·mongodb·kingbase
Re.不晚2 小时前
Redis——主从复制
数据库·redis·缓存
小高不会迪斯科10 小时前
CMU 15445学习心得(二) 内存管理及数据移动--数据库系统如何玩转内存
数据库·oracle
e***89010 小时前
MySQL 8.0版本JDBC驱动Jar包
数据库·mysql·jar
l1t10 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
失忆爆表症12 小时前
03_数据库配置指南:PostgreSQL 17 + pgvector 向量存储
数据库·postgresql