崩溃恢复必须先redo再undo,因为需先用redo log将所有已记录的物理变更重放到数据页,确保页状态完整可逆;若先undo,未提交事务的中间修改可能已部分刷盘且未被覆盖,导致数据页处于错误中间态,破坏原子性。

崩溃恢复时为什么必须先 redo 再 undo
因为恢复的目标是让数据页回到崩溃前的“最新一致状态”,而不是直接回滚。如果先执行 undo log,未提交事务的中间修改可能已被部分刷盘,又没被重放覆盖,会导致内存页处于错误中间态——破坏原子性。只有先用 redo log 把所有已写入日志的物理变更(无论事务是否提交)全部重放到数据页上,才能确保页状态完整、可逆。
常见错误现象:人为删除或截断 ib_logfile* 后强行启动 MySQL,redo log 扫描起点错乱,导致重做阶段卡在 redo log parsing completed 之前,实例 hang 住无法完成恢复。
- 恢复顺序不可配置、不可跳过,由 InnoDB 启动逻辑硬编码决定
-
redo log重做不依赖undo log是否可用;但undo log回滚必须基于重做后的页状态 - 若
undo log段损坏(如ibdata1尾部坏块),恢复可能在第二阶段报ERROR 1030 (HY000): Got error 168 from storage engine
Redo Phase 实际做了什么
这个阶段只读取并应用 redo log 中从 checkpoint LSN 开始的所有日志记录,对 Buffer Pool 中对应的数据页执行原样修改(比如把 page 123 offset 456 处的字节从 0x11 改成 0x22)。它不解析 SQL,不查事务状态,也不写磁盘——只是把日志里记的物理操作再执行一遍。
容易踩的坑:innodb_flush_log_at_trx_commit=0 或 =2 时,崩溃后会丢失部分已提交事务的 redo log 记录,这部分事务无法恢复,不是 bug,是配置明确放弃持久性保障的结果。
- 日志扫描起点来自
ibdata1中存储的最后 checkpoint 位置,不是从头读整个ib_logfile* - 重做耗时主要取决于脏页数量和磁盘 I/O 性能,SSD 上通常秒级;HDD 上若待重做记录超百万条,可能持续数分钟
- 日志中出现大量
Applying log record行,且紧随Starting crash recovery from checkpoint LSN=...,说明正在重做
Undo Phase 如何识别并清理未提交事务
重做完成后,InnoDB 扫描 undo log 段,查找所有标记为 ACTIVE 的事务(即没有 TRX_UNDO_CACHED 或 TRX_UNDO_TO_FREE 状态的事务),用其记录的前镜像执行反向操作:对 INSERT 做 DELETE,对 UPDATE 做逆向 UPDATE,对 DELETE 做反向 INSERT。
关键点在于:这个阶段只处理未提交事务,已提交事务的修改已在 Redo Phase 固化,不会被 undo 影响。
- long running transaction 会阻塞
undo log清理,导致ibdata1持续膨胀,甚至触发ERROR 1114 (HY000): The table is full - 可通过
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), TRX_STARTED)) > 60快速定位运行超 60 秒的事务 -
innodb_undo_tablespaces配置启用独立 undo 表空间后,ibdata1不再承载 undo 段,避免共享表空间无限增长
两次写(double write)如何配合 redo 恢复
redo log 只能修复“内容正确但未刷盘”的页,无法修复“写了一半就崩溃”的半页(torn page)。这时候 dblwr 文件起关键作用:InnoDB 刷脏页前,会先将完整页拷贝到 #ib_*.dblwr 文件中。崩溃重启后,MySQL 先加载这些 dblwr 页面,用它们修复损坏的数据页,再应用 redo log。
典型问题:若 innodb_doublewrite=OFF,且磁盘发生部分写失败,崩溃恢复时可能因数据页损坏而直接报错退出,无法继续。
-
dblwr文件默认位于 MySQL 数据目录下,文件名形如#ib_16384_0.dblwr - 恢复过程中若发现某数据页校验失败(checksum mismatch),会尝试从
dblwr中找对应页副本进行替换 - 该机制与
redo log协同,构成崩溃恢复的底层安全边界,不可绕过
实际恢复链条非常紧凑:checkpoint LSN → 加载 dblwr → 修复损坏页 → 重放 redo → 构建完整内存页 → 扫描 undo → 回滚活跃事务。任何一环缺失或损坏,都会中断恢复流程。最常被忽略的是 dblwr 文件权限异常或磁盘空间满导致其写入失败——此时即使 redo log 完好,也无法完成页修复。


















