MySQLNotes

第 17 章:Undo Log 与 Redo Log

zjc 于 2026-01-17 发布

这是《MySQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 InnoDB 用 Redo Log 保证崩溃恢复,用 Undo Log 支持回滚和 MVCC。两者名字相似,方向相反:Redo 记录“如何重做已提交物理变更”,Undo 记录“如何撤销逻辑变更”。

17.1 两份日志的分工

日志 方向 主要作用 内容特征
Redo Log 前滚 崩溃后恢复已提交修改 物理页相关变更
Undo Log 回滚 回滚事务、MVCC 版本链 逻辑反操作

示例:

UPDATE accounts
SET balance = balance + 100
WHERE id = 1;

Undo 记录逻辑上的旧值:

balance 旧值 -> 500

Redo 记录页面变更:

某个数据页某偏移位置修改为某内容

17.2 WAL:先写日志再写数据页

InnoDB 遵循 Write-Ahead Logging:

1. 修改 Buffer Pool 中的数据页;
2. 生成 Redo;
3. 事务提交前持久化 Redo;
4. 数据页可以稍后刷盘;
5. 崩溃后用 Redo 重放已提交变更。

好处:

  1. 顺序写替代随机写;
  2. 提交时不必立刻刷所有数据页;
  3. 提交持久性由较小日志保证;
  4. Buffer Pool 可以合并多次修改;
  5. 崩溃恢复有明确边界。

17.3 Redo Log 结构

查看参数:

SHOW VARIABLES LIKE 'innodb_log_file_size';
SHOW VARIABLES LIKE 'innodb_log_files_in_group';
SHOW VARIABLES LIKE 'innodb_log_buffer_size';

MySQL 8.0.30+ 更推荐:

SHOW VARIABLES LIKE 'innodb_redo_log_capacity';

循环写入:

write pos:当前写入位置
checkpoint:已刷盘数据页对应的水位
write pos 追上 checkpoint 前必须推进 checkpoint

如果 Redo 空间不足:

  1. 刷脏页加剧;
  2. 写入抖动;
  3. checkpoint 阻塞;
  4. Log file is nearly full
  5. 响应时间尖刺。

查看状态:

SHOW ENGINE INNODB STATUS\G

关注:

LOG
---
Log sequence number
Log flushed up to
Last checkpoint at

17.4 innodb_flush_log_at_trx_commit

参数取值:

行为 持久性 性能
1 每次提交刷 Redo 到磁盘 最强 最低
2 提交写到 OS cache,每秒刷盘 MySQL 崩溃通常不丢,OS 崩溃可能丢 较高
0 每秒写并刷盘 崩溃可能丢事务 最高

查看:

SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';

订单、支付、账户主库建议:

innodb_flush_log_at_trx_commit = 1
sync_binlog = 1

非核心日志库可以结合业务接受度评估 2 或 0,但必须明确故障窗口内可能丢数据。

17.5 Redo 与 Binlog 的区别

项目 Redo Log Binlog
所属层 InnoDB Server 层
内容 物理页变更 逻辑变更
用途 崩溃恢复 复制、归档、时间点恢复
写法 循环覆盖 追加文件
引擎 InnoDB 所有引擎
记录单位 页修改 SQL / 行变更

Binlog 常见格式:

格式 特点
STATEMENT 记 SQL,体积小,但部分函数和不确定性语句有风险
ROW 记行镜像,一致性最好,大批量修改日志较大
MIXED 由 MySQL 选择,历史过渡方案

查看:

SHOW VARIABLES LIKE 'binlog_format';
SHOW BINARY LOGS;
SHOW BINLOG EVENTS IN '<log_file_name>' LIMIT 10;

17.6 Undo Log 类型

类型 场景
insert undo INSERT 产生,事务提交后可较快清理
update undo UPDATE / DELETE 产生,需要服务 MVCC
purge undo 清理 delete-mark 记录和历史版本

INSERT 的旧版本通常不需要被其他事务读取,提交后清理成本较低。UPDATE / DELETE 的旧版本可能被长事务需要,因此保留更久。

17.7 Undo 与回滚

大事务回滚:

START TRANSACTION;

UPDATE orders
SET status = 'CANCELED'
WHERE created_at < '2025-01-01';

ROLLBACK;

回滚需要按 Undo 执行反向操作:

  1. 恢复旧值;
  2. 清理索引修改;
  3. 释放锁;
  4. 维护事务状态;
  5. 记录进度。

大事务回滚可能比执行更久,期间锁和 Undo 仍在。千万不要以为 ROLLBACK 能瞬间撤销所有影响。

查看回滚中的事务:

SELECT
  trx_id,
  trx_state,
  trx_started,
  trx_rows_modified,
  trx_mysql_thread_id
FROM information_schema.INNODB_TRX;

17.8 Undo 表空间治理

MySQL 8.0 默认使用独立 undo 表空间:

SHOW VARIABLES LIKE 'innodb_undo_directory';
SHOW VARIABLES LIKE 'innodb_undo_tablespaces';
SHOW VARIABLES LIKE 'innodb_undo_log_truncate';
SHOW VARIABLES LIKE 'innodb_max_undo_log_size';

查看:

SELECT
  space,
  name,
  file_type
FROM information_schema.INNODB_TABLESPACES
WHERE name LIKE '%undo%';

膨胀原因:

  1. 长事务;
  2. 高频 UPDATE;
  3. 大事务;
  4. Purge 延迟;
  5. 机器 IO 瓶颈;
  6. 回滚大事务。

处理顺序:

1. 找最老事务;
2. 确认业务是否可终止;
3. 拆分大任务;
4. 优化热点更新;
5. 观察 History list;
6. 最后才考虑空间参数和扩容。

17.9 Buffer Pool 与脏页

查看:

SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW STATUS LIKE 'Innodb_buffer_pool_wait_free';
SHOW STATUS LIKE 'Innodb_data_pending_writes';

脏页来源:

  1. 数据页修改;
  2. 索引页修改;
  3. Undo 页;
  4. Change Buffer 合并;
  5. Adaptive Hash 元数据。

刷脏触发:

  1. Redo 空间不足;
  2. Buffer Pool 空闲页不足;
  3. 后台定期刷新;
  4. checkpoint;
  5. 关闭实例;
  6. 手动刷新。

手动触发:

FLUSH TABLES;
SET GLOBAL innodb_max_dirty_pages_pct = 75;

生产不应频繁手动刷脏,需以监控和容量规划为主。

17.10 Doublewrite Buffer

Redo 记录“对某页应用某变更”,而不是完整页面镜像。如果页本身发生部分写坏,仅靠 Redo 无法安全重放。

Doublewrite 流程:

1. 脏页先写入 doublewrite 区域;
2. 刷盘;
3. 再写入表空间对应位置;

崩溃后发现页损坏时,可从 doublewrite 恢复完整页,再应用 Redo。

查看:

SHOW VARIABLES LIKE 'innodb_doublewrite';
SHOW STATUS LIKE 'Innodb_dblwr_pages_written';

关闭 Doublewrite 可能提升一点性能,但以牺牲部分写保护为代价。生产默认保持开启。

17.11 崩溃恢复流程

简化流程:

1. 读取最近 checkpoint;
2. 扫描 Redo;
3. 重放必要页面变更;
4. 恢复 Undo;
5. 根据事务状态回滚未提交事务;
6. 与 Binlog 协调 prepared 事务;
7. 实例可对外服务。

大事务、大量脏页、Redo 很长、IO 故障都会拉长恢复时间。高可用设计必须考虑重建时间,而不是只关心平时 QPS。

17.12 生产实践

  1. 核心主库保持双 1;
  2. 控制事务大小;
  3. 避免大范围 UPDATE / DELETE;
  4. 监控 Redo 等待和 checkpoint;
  5. 监控 Undo 表空间和 History list;
  6. 大任务按主键或时间分批;
  7. 压测新参数;
  8. 备份和恢复演练必须覆盖崩溃场景;
  9. 磁盘必须使用掉电保护或可靠云盘;
  10. 不用非持久化参数换取虚假性能。

本章小结

Redo Log 通过 WAL 和崩溃恢复保证已提交修改的持久性,Undo Log 支持事务回滚和 MVCC。innodb_flush_log_at_trx_commit=1 提供最强持久性;Doublewrite 解决部分写坏页问题;长事务和大事务会拖累 Undo、Purge、锁和恢复时间。

思考题

  1. Redo Log 和 Undo Log 的方向有什么区别?
  2. 为什么提交时刷 Redo 而不是刷所有数据页?
  3. innodb_flush_log_at_trx_commit 三个取值分别意味着什么?
  4. Doublewrite Buffer 解决什么问题?
  5. 大事务回滚为什么可能很慢?如何避免?