MySQL崩溃后靠redo log恢复数据,其通过重放未落盘的页变更将数据页追平至崩溃前状态,binlog仅用于主从复制和时间点恢复,不参与崩溃恢复。

MySQL崩溃后靠什么恢复数据
MySQL崩溃后能恢复到最近一次一致状态,核心依赖的是 redo log(重做日志)和 binlog(归档日志)的协同机制。其中 redo log 是 InnoDB 存储引擎层的物理日志,保障事务的持久性(DURABLE);binlog 是 Server 层的逻辑日志,用于主从复制和基于时间点的恢复。两者不互相替代,但配合使用才能实现崩溃安全与可回溯。
为什么只靠 binlog 不能保证崩溃恢复
binlog 是追加写、逻辑日志(如 INSERT INTO t VALUES (1)),它不记录页级变更,也不保证写入时机与事务提交强绑定。如果 MySQL 在事务已写 binlog 但尚未刷盘 InnoDB 数据页时崩溃,重启后会发现:数据页仍是旧值,而 binlog 里却有这条“已提交”的记录——造成主从不一致或恢复出错。
-
binlog默认异步写入磁盘(sync_binlog=0),崩溃可能丢失未刷盘的 binlog 内容 - InnoDB 数据页刷新(
flush)是后台异步进行的,事务提交时并不保证数据页已落盘 - 只有
redo log在事务commit前强制写入并fsync到磁盘(由innodb_flush_log_at_trx_commit控制),确保崩溃后可用其重放未落盘的页变更
崩溃恢复时 redo log 怎么工作
MySQL 启动时,InnoDB 会自动进入恢复流程:扫描 redo log 文件(通常是 ib_logfile0 和 ib_logfile1),从检查点(checkpoint)位置开始,重放所有已写入但未应用到数据页的 redo log 记录,把数据页“追平”到崩溃前的状态。
- 检查点位置由
innodb_checkpoint_lsn记录,代表已刷盘数据页对应的日志序列号(LSN) - 恢复过程不依赖
binlog,纯靠redo log的物理操作(如“将 page 123 offset 48 处的 4 字节改为 0x00000001”) - 若
innodb_flush_log_at_trx_commit = 1(默认),每次COMMIT都触发fsync,崩溃最多丢失 1 个事务;设为 0 或 2 时,可能丢失秒级事务
mysql> SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit'; +--------------------------------+-------+ | Variable_name | Value | +--------------------------------+-------+ | innodb_flush_log_at_trx_commit | 1 | +--------------------------------+-------+
如何验证 redo log 是否正常参与恢复
最直接的方式是模拟崩溃并观察错误日志。手动 kill -9 mysqld 进程后重启,查看 error.log 中是否出现类似以下内容:
InnoDB: Starting crash recovery. InnoDB: Reading tablespace information from the .ibd files... InnoDB: Doing recovery: scanned up to log sequence number XXXXXXXXX InnoDB: Database was not shut down normally!
注意几个关键点:
- 必须用
kill -9(非mysqladmin shutdown),否则不会触发 crash recovery 流程 - 确保
innodb_log_file_size足够大(如 256M),避免频繁覆盖导致早期日志丢失 - 不要手动删除或清空
ib_logfile*,否则 InnoDB 启动会报错Invalid log file size并拒绝启动
真正容易被忽略的是:redo log 和 binlog 的两阶段提交(2PC)协议是否启用。只要 innodb_support_xa = ON(5.7.7+ 默认开启)且 sync_binlog = 1,MySQL 就会在事务提交时先写 redo log prepare,再写 binlog,最后写 redo log commit —— 这个顺序才是崩溃后能对齐两者的关键。漏掉任一环节,恢复就不可靠。










