这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Redo log 记录物理页修改,用于崩溃恢复时重做到已提交状态。InnoDB 采用 WAL 思路:先记录日志,再择机刷脏页。
19.1 WAL
modify page
-> write redo log buffer
-> write log file
-> fsync as needed
-> later flush dirty page
崩溃后:
load page
-> if page LSN older
-> apply redo from checkpoint
19.2 MTR
mini transaction 是物理一致性单位:
- 修改多个页;
- 写一组 redo;
- 保证页级操作原子性;
- 持有相关 latch;
- 提交时写 log buffer。
示例:
insert row
-> modify leaf page
-> maybe update page directory
-> mtr commit writes redo records
19.3 LSN
LSN 表示日志序列号:
| 位置 | 含义 |
|---|---|
| log buffer 末尾 | 已写入内存 |
| flushed LSN | 已写盘 |
| checkpoint LSN | 前缀可以不再重做 |
| page LSN | 页面最新修改 |
checkpoint 推进依赖脏页刷盘和旧版本清理。
19.4 刷盘策略
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
| 值 | 行为 |
|---|---|
| 1 | 每次提交 fsync |
| 0 | 延迟写和刷 |
| 2 | 提交写 OS cache,不强制 fsync |
值为 1 最利于持久性;0/2 在故障时可能丢事务,需业务接受。
19.5 Redo 容量
SHOW VARIABLES LIKE 'innodb_redo_log_capacity';
SHOW GLOBAL STATUS LIKE 'Innodb_redo_log%';
容量小的影响:
- checkpoint 频繁;
- 脏页刷盘压力放大;
- 写入抖动;
- 用户线程被迫 flush。
容量大也不是无限收益,要结合恢复时间和磁盘能力评估。
19.6 组提交
多个事务提交时可以合并 log flush:
trx1 fsync
trx2 fsync
trx3 fsync
-> group fsync
收益依赖并发、设备延迟和 binlog sync 策略。
相关:
SHOW VARIABLES LIKE 'sync_binlog';
sync_binlog=1 与 redo 双 1 组合提供更强的持久性保障。
19.7 恢复
start
-> read checkpoint
-> scan redo
-> build page hash
-> apply redo
-> rollback uncommitted trx
undo 恢复负责回滚未提交事务,redo 负责重做已提交修改。
19.8 常见问题
| 现象 | 方向 |
|---|---|
| commit 延迟高 | redo fsync、binlog sync |
| checkpoint age 高 | redo 容量、脏页、IO |
| 刷脏抖动 | io_capacity、redo 压力 |
| 恢复时间长 | redo 量、脏页、大事务回滚 |
| 写入尖刺 | checkpoint、merge、统计任务 |
19.9 源码入口
| 文件 | 职责 |
|---|---|
log0log.cc |
redo 核心 |
mtr0mtr.cc |
mini transaction |
buf0flu.cc |
checkpoint 相关刷脏 |
log0recv.cc |
恢复 |
断点:
b mtr_t::commit
b log_write_flush_to_disk
b log_checkpoint
本章小结
Redo 通过 WAL 保证页修改可恢复。MTR 组织物理修改,LSN 串联日志与页面状态,checkpoint 依赖脏页刷盘推进。刷盘策略、redo 容量和 IO 能力共同决定写入延迟。
思考题
- 为什么修改页前先写 redo?
- MTR 和事务有什么区别?
- checkpoint 为什么依赖脏页刷盘?
innodb_flush_log_at_trx_commit=2在断电时可能发生什么?- redo 容量过大有什么代价?