[小技巧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,解决空间膨胀问题;
  • 提升并发事务处理能力(更多回滚段)。
相关推荐
IvorySQL8 小时前
PostgreSQL 日报 |SUM-AVG 溢出检测不足(9 月 5 日)
数据库·postgresql·区块链
2601_962099088 小时前
python flask sqlalchemy连接数据库流程介绍
数据库·python·flask·教程·sqlalchemy
TDengine (老段)8 小时前
TDgpt 概览 — AI 增强的时序数据库
大数据·数据库·人工智能·物联网·时序数据库·iot·tdengine
云贝贝贝8 小时前
MySQL 运维高频 6 坑:字符集、超时、SQL_MODE、隐式转换、Online DDL、主从延迟
运维·sql·mysql
2501_933670798 小时前
采购运营校招Excel能力清单:函数、透视表、ERP数据与SQL入门
数据库·sql·excel
YOLO数据集集合8 小时前
红外热成像人员检测数据集 |红外检测 白热模式 人员检测 夜间监控 目标检测 YOLO格式 深度学习数据集 计算机视觉9067期
数据库·深度学习·yolo·目标检测·计算机视觉·红外人员·红外数据
BUG指挥官8 小时前
Redis已接入AI
数据库·人工智能·spring boot·redis·spring cloud
SkyWalking中文站17 小时前
BanyanDB 0.11.0:新特性与升级指南
数据库
₍˄·͈༝·͈˄*₎◞ ̑̑码19 小时前
MyBatis操作数据库
数据库·mybatis
刃神太酷啦20 小时前
Redis 进阶核心:持久化 (RDB/AOF)、事务与主从复制全解析----《Hello Redis!》(5)
linux·c语言·数据库·c++·redis·缓存·bootstrap