InnoDB 用 WAL(Write-Ahead Logging)机制实现高性能写入和 crash-safe 能力,核心是 redo log(物理日志)和 binlog(逻辑日志)的协作,两者通过两阶段提交保持一致。
目录
| 章节 | 说明 |
|---|---|
| WAL 机制 | 先写日志再写磁盘,为什么能提升性能 |
| redo log | InnoDB 专属,保障 crash-safe |
| binlog | Server 层归档日志,用于复制和恢复 |
| redo log vs binlog | 三个关键区别 |
| 两阶段提交 | 为什么必须两阶段 |
| 关键参数 | 生产环境推荐配置 |
| 数据恢复流程 | 利用 binlog 恢复到任意时间点 |
WAL 机制
WAL = Write-Ahead Logging:先写日志,再写磁盘。
掌柜的粉板(内存 + redo log) vs 账本(磁盘数据文件)
写粉板(快)→ 等空闲时再更新账本(慢但不阻塞业务)
为什么能提升性能?
- 直接更新磁盘:随机 I/O,需要先找到记录位置再修改
- WAL 写日志:顺序 I/O,直接追加写,速度快 10 倍以上
redo log
是什么
- InnoDB 引擎专属的物理日志,记录"在某个数据页上做了什么修改"
- 固定大小、循环写(不是追加写)
- 保障 crash-safe:即使数据库异常重启,已提交的事务不会丢失
环形结构
- write pos:当前写入位置,一直往后推进,到末尾回到开头
- checkpoint:已经刷到磁盘的位置,往后推进代表擦除可复用空间
- write pos 追上 checkpoint → redo log 写满 → 必须先刷脏页,才能继续写
redo log 满了会怎样
redo log 写满时,系统停止所有更新操作,强制把 checkpoint 往前推(将对应脏页刷盘),腾出空间。这就是 MySQL 偶尔"抖一下"的原因之一。
MySQL 8.0.30+ 的新文件架构
8.0.30 起 redo log 文件机制做了重大改变:
| 8.0.30 以前 | 8.0.30+ | |
|---|---|---|
| 文件位置 | 数据目录根下(ib_logfile0、ib_logfile1) |
#innodb_redo/ 子目录 |
| 文件数量 | 默认 2 个 | 32 个(16 活跃 + 16 spare _tmp) |
| 容量配置 | innodb_log_files_in_group × innodb_log_file_size |
innodb_redo_log_capacity(默认 100MB) |
| 在线调整 | ❌ 需重启 | ✅ SET GLOBAL innodb_redo_log_capacity=... |
为什么用 32 个小文件而不是 2 个大文件?
官方原文(Frederic Descamps,MySQL 官方博客,2022-12-24,经 InnoDB 团队 Kuba Lopuszanski 审阅):
"InnoDB tries to maintain approximately 32 files here, so that it doesn't need to wait long before one of them becomes no longer needed as it would if you had just 2 big files. This way it can reclaim them one by one when you want to resize them."
核心动机是回收粒度更细:2 个大文件必须等整个文件都不再需要才能释放,32 个小文件可以逐个回收,resize 时等待时间大幅缩短。
_tmp 文件是预备(spare)文件,等待被激活使用;活跃文件用完后直接从 spare 中补充,无需临时分配磁盘空间。
⚠️ undersized redo log 会导致性能问题(write_pos 频繁追上 checkpoint);oversized 则增加 crash recovery 和 shutdown 耗时。建议在业务高峰期实测后设置合适容量。
binlog
是什么
- MySQL Server 层实现的逻辑日志,所有存储引擎都可以使用
- 记录"这个语句的原始逻辑",比如"给 ID=2 这一行的 c 字段加 1"
- 追加写,不会覆盖历史日志,文件写满后切换到下一个文件
两种格式
| 格式 | 内容 | 特点 |
|---|---|---|
statement |
原始 SQL 语句 | 日志小,但某些语句(含函数)可能主从不一致 |
row |
每行数据的变化 | 日志大,但主从一致性最好,推荐生产使用 |
mixed |
自动切换 | 折中方案 |
redo log vs binlog
为什么 InnoDB 要有自己的 redo log? MySQL 最早只有 MyISAM,没有 crash-safe 能力。InnoDB 作为插件引入后,无法依赖 binlog(binlog 不支持 crash-safe),因此自己实现了 redo log。
两阶段提交
为什么必须两阶段
如果不用两阶段提交,会出现数据不一致:
| 场景 | 问题 |
|---|---|
| 先写 redo log,再写 binlog,中间 crash | redo log 有记录,数据恢复后 c=1;但 binlog 没有,用 binlog 恢复的临时库 c=0,主从不一致 |
| 先写 binlog,再写 redo log,中间 crash | binlog 有记录,用它恢复的库 c=1;但 redo log 没有,crash 恢复后 c=0,主从不一致 |
本质:两阶段提交让 redo log 和 binlog 形成逻辑上的原子操作,要么都有要么都没有。
关键参数
-- 查看 redo log 刷盘策略(建议设为 1)
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
-- 查看 binlog 刷盘策略(建议设为 1)
SHOW VARIABLES LIKE 'sync_binlog';
| 参数 | 值 | 含义 |
|---|---|---|
innodb_flush_log_at_trx_commit |
1(推荐) |
每次事务提交都将 redo log 持久化到磁盘 |
innodb_flush_log_at_trx_commit |
2 |
每秒一次刷盘,性能更好但可能丢 1 秒数据 |
sync_binlog |
1(推荐) |
每次事务提交都将 binlog 持久化到磁盘 |
sync_binlog |
0 |
由 OS 决定刷盘时机,性能好但可能丢数据 |
生产环境强烈建议两个参数都设为
1,保证 MySQL 异常重启后数据不丢失。
数据恢复流程
恢复到任意时间点
前提:
- 定期全量备份(如每天凌晨)
- 保留全量备份时间点之后的所有 binlog
恢复步骤:
- 找到最近的全量备份,恢复到临时库
- 从备份时间点开始,依次重放 binlog
- 重放到目标时刻(如误删前 1 秒)
- 将数据从临时库导回线上库
全量备份周期的选择
| 备份周期 | 优点 | 缺点 |
|---|---|---|
| 每天备份 | RTO 短(最多应用 1 天 binlog) | 存储成本高 |
| 每周备份 | 存储成本低 | RTO 长(最坏需应用 1 周 binlog) |
RTO(Recovery Time Objective):数据恢复所需的最长时间,是衡量备份策略的核心指标。
参考资料
- 《MySQL 实战 45 讲》— 第 02 讲:日志系统:一条 SQL 更新语句是如何执行的?
- 《MySQL 实战 45 讲》— 第 23 讲:MySQL 是怎么保证数据不丢的?
- InnoDB Redo Log
评论 (0)
发表评论