redo log崩溃恢复只重做已提交但未刷盘的修改,未提交事务由undo log回滚;innodb通过checkpoint的last_checkpoint_lsn定位起始位置,顺序扫描并重放对应物理页变更,全程无sql解析,仅机械覆写字节。

MySQL事务执行过程中,Redo Log崩溃恢复只负责“重做已提交但未刷盘的修改”,不处理未提交事务——这部分由Undo Log回滚。它不是备份,也不是SQL日志,而是一份物理页变更快照。
崩溃后InnoDB怎么知道该重放哪些Redo Log?
启动时,InnoDB会读取ib_logfile0头部的checkpoint信息,定位到last_checkpoint_lsn——这是最后一次刷盘成功的脏页状态标记。从这个LSN开始顺序扫描所有ib_logfile*,过滤出其中已提交(commit标记存在)且对应数据页尚未刷入磁盘的记录。
关键点:
- LSN是字节级偏移量,不是时间戳,保证重放顺序严格一致
- 检查点(checkpoint)越新,需要重放的日志越少,恢复越快
- 如果
innodb_log_file_size太小(如默认48MB),checkpoint频繁触发,写入抖动大;太大(如2GB)则恢复可能耗时数分钟
Redo Log重放时到底在做什么操作?
它不做SQL解析,不查索引,不校验约束,只按物理地址执行“覆写”:
- 读取一条redo记录:例如
page 123 offset 48, 4 bytes: 0x1234 → 0x5678 - 定位到Buffer Pool中对应的16KB页(若不在内存,则先从.ibd或doublewrite文件加载)
- 直接将指定偏移处的字节替换成新值
- 更新该页的
page_lsn为当前redo记录的LSN
这个过程叫“前滚(roll forward)”,全程无事务语义,纯机械式应用。所以它快,也所以它不能修复逻辑错误(比如误删、错UPDATE)。
为什么有些已提交事务还是丢了?
根本原因在于innodb_flush_log_at_trx_commit配置不当:
-
=1(默认):每次COMMIT都调用fsync()落盘 → 安全,但有IO开销 -
=2:写入OS page cache即返回,后台每秒fsync()→ OS崩溃或断电时,cache中未刷盘的redo丢失 -
=0:仅写入InnoDB log buffer,后台线程每秒刷一次 → kill -9或断电必丢最近1秒所有已提交事务
注意:binlog开启与否完全不影响Redo Log是否生效;binlog只用于主从复制和时间点恢复,不参与崩溃恢复。
Doublewrite Buffer和Redo Log是什么关系?
Doublewrite是Redo Log能正确工作的前提之一:
- Redo Log假设“目标数据页本身是完整的”,但它无法修复“写半页(torn page)”——比如刷页时断电,只写了前8KB
- Doublewrite先将脏页完整写入
#ib_16384_0.dblwr等临时文件,再刷入真实表空间;崩溃后,InnoDB优先用dblwr里的完整页覆盖损坏页,再用Redo Log前滚 - 若
innodb_doublewrite=OFF且发生torn page,即使Redo Log完整,重放也会失败(因为基础页已损坏)
真正容易被忽略的是:Redo Log恢复必须依赖doublewrite保障页完整性,二者是协同链条,缺一不可。单独调优innodb_flush_log_at_trx_commit却关闭doublewrite,等于给安全气囊装了个漏气阀。











