undo log 是 InnoDB 为保障事务原子性和 MVCC 而生成的逻辑回滚日志,记录 INSERT/UPDATE/DELETE 前的数据状态,事务首条修改语句执行前即开始写入,按链式逆序执行反操作实现回滚。

undo log 是 InnoDB 存储引擎为保证事务原子性和实现 MVCC(多版本并发控制)而生成的逻辑回滚日志。它不是“备份文件”,也不是“快照副本”,而是记录了每条 INSERT、UPDATE、DELETE 操作之前的数据状态,用于在事务失败或显式执行 ROLLBACK 时,把数据“逻辑还原”回去。
undo log 怎么被写入?什么时候开始记?
它在事务**第一条修改语句执行前就已启动记录**——不是等 COMMIT 或 ROLLBACK 才写,而是边改边记。
-
INSERT:记录主键值,回滚时执行DELETE -
DELETE:记录整行旧值,回滚时执行INSERT -
UPDATE:记录被修改列的旧值 + 主键(或唯一标识),回滚时执行反向UPDATE
注意:SELECT 不产生 undo log;只有 DML 修改操作才触发。
它先写入内存中的 undo buffer,再按需刷盘到 ibdata 或独立的 undo tablespace(MySQL 8.0+ 默认启用)。
事务回滚时 undo log 真正怎么工作的?
回滚不是“从磁盘读旧数据再覆盖”,而是**按 undo log 链逆序执行逻辑反操作**:
- 事务 A 修改了 3 行,生成 3 条 undo log,按时间倒序连成链
- 执行
ROLLBACK时,InnoDB 从链尾往前遍历,逐条“重放”对应的逆操作 - 比如某条 undo log 记着
UPDATE t SET x=100 WHERE id=5→ 回滚时就做UPDATE t SET x=旧值 WHERE id=5
整个过程不依赖外部备份,也不需要锁表;但要求 undo log 未被 purge 线程清理掉——所以长事务会阻塞 purge,导致 undo log 膨胀、空间无法回收。
常见配置和容易踩的坑
MySQL 8.0+ 中,undo log 默认使用独立表空间,可通过以下参数控制行为:
-
innodb_undo_directory:指定存放路径,默认是datadir,建议单独挂 SSD 目录 -
innodb_undo_tablespaces:undo 表空间个数,影响并行 purge 效率 -
innodb_undo_log_truncate:设为1才允许在线截断(收缩)过大的 undo 文件 -
innodb_max_undo_log_size:单个 undo 表空间最大大小,超限后才触发 truncate(需配合上一项)
典型翻车场景:
- 开启
innodb_undo_log_truncate=0(默认关闭),结果大事务跑完,undo 占满几十 GB 也无法自动缩容 - 把
innodb_undo_directory指向 NFS 或慢盘,导致高并发更新时 undo 写入成为瓶颈 - 长时间不提交的事务(如应用端忘记
commit),让 undo log 一直保留,MVCC 版本链拉长,拖慢其他查询性能
undo log 和 redo log、binlog 的关键区别在哪?
三者完全不是同一层的东西:
-
redo log:物理日志,记录“页号+偏移+修改后字节”,用于崩溃恢复,保障持久性 -
undo log:逻辑日志,记录“SQL 逆操作”,用于回滚和 MVCC,保障原子性与隔离性 -
binlog:Server 层日志,记录逻辑 SQL 或行事件,用于主从复制和归档,不参与崩溃恢复
特别注意:undo log 本身也受 redo log 保护——即 undo 的写入操作也会生成 redo,否则 undo 损坏就无法回滚了。
真正要管好 undo log,核心就三点:别让事务太长、别关 innodb_undo_log_truncate、别把它和 redo/binlog 混为一谈。它不显眼,但一旦出问题,轻则慢查询,重则磁盘爆满、回滚卡死。










