概述
"先投递到智能柜,再批量入库"------InnoDB 如何用日志实现又快又稳的写入?
在 MySQL 的 InnoDB 存储引擎中,redo log(重做日志) 是实现 高性能写入 与 崩溃安全(Crash-Safe) 的核心机制。
但很多开发者只知道"它很重要",却不明白:
- 它到底怎么工作?
- 为什么能提升性能?
- 断电后如何恢复数据?
- 和 binlog 有什么关系?
本文用一个 "智能快递柜 + 中央仓库" 的生活化类比贯穿始终,带你彻底搞懂 redo log。
一🔧 Redo Log 是磁盘文件,不是内存结构
📌 重点澄清:redo log 不是只存在于内存的临时日志,而是写入磁盘的物理文件。
💡 为什么这一点很重要?
- 很多人以为:"redo log 在内存里,断电就没了"
- 实际上:只要
fsync()成功,redo log 就已经落盘到磁盘文件,即使 MySQL 崩溃、服务器断电,数据依然可恢复
📁 Redo Log 的磁盘文件结构
默认情况下,在 MySQL 数据目录下(如 /var/lib/mysql/)会看到两个文件:
text
ib_logfile0
ib_logfile1
这两个文件就是 redo log 的物理存储文件,它们:
- 每个大小固定(可通过
innodb_log_file_size配置) - 默认为 2 个文件,形成环形缓冲(circular buffer)
- 预分配空间,不会动态增长
❗ 常见误区澄清
❌ 错误理解:
"redo log 是内存结构,断电就丢"
✅ 正确理解:"redo log 最终是写入磁盘文件
ib_logfile0/1的,只要fsync()成功,就算断电也不丢。"
🔍 举个例子:如果你在银行取钱,柜员给你一张小票(内存日志),但还没盖章确认(没 fsync),突然停电------这笔交易可能作废。
但如果柜员已经把小票放入保险箱(磁盘文件),哪怕他下班走人,交易也是有效的。
✅ 如何验证 redo log 文件存在?
bash
ls -lh /var/lib/mysql/ib_logfile*
输出示例:
text
-rw------- 1 mysql mysql 1.0G Sep 15 10:00 ib_logfile0
-rw------- 1 mysql mysql 1.0G Sep 15 10:00 ib_logfile1
👉 可以看到,这两个文件确实存在于磁盘上,且大小为 1GB。
二、为什么需要 Redo Log?------从"仓库效率"说起
🏭 场景:传统仓库的困境
想象你是一家电商的仓库管理员:
- 中央仓库(磁盘
.ibd文件) :存放所有商品,但入库/出库流程复杂,每次操作都要开锁、登记、搬运------非常慢。 - 用户下单修改商品信息(比如"库存 -1"),你必须:
- 找到对应货架(数据页)
- 修改商品标签(字段值)
- 立刻把整个货架重新封存入库(刷 16KB 页)
⚠️ 即使只改一个数字,也得动整个货架!
📉 问题:高并发 = 仓库瘫痪
- 1000 个用户同时下单 → 1000 次随机货架操作
- 仓库通道拥堵,管理员忙不过来
- 用户等得着急:"怎么还没确认?"
这就是直接刷数据页的性能瓶颈 :随机 I/O 成为系统瓶颈。
✅ 解法:引入"智能快递柜"系统
InnoDB 的聪明做法是:
- 在仓库门口安装一组 智能快递柜(redo log)
- 管理员(InnoDB 引擎)收到订单后:
- 在内存清单(Buffer Pool)中修改商品信息
- 立即生成一张"投递单",放进快递柜(写 redo log)
- 马上告诉用户:"订单已处理!"
- 稍后(系统空闲时),再批量把快递柜里的包裹整理入库(刷脏页)
💡 优势:
- 快递柜支持 高速、顺序投递(顺序写),远快于随机入库
- 即使仓库突然断电,只要快递柜记录还在,就能恢复所有未入库的订单
- 用户体验好:响应快 + 数据不丢
✅ 这就是 redo log 的核心价值:用"小而快的日志写入",换取"大而慢的数据页异步刷盘"。
三、Redo Log 的三层写入流程(快递柜的"三道保险")
很多人以为"放进快递柜 = 安全",其实快递柜本身也有 三层防护:
scss
┌──────────────────────┐
│ 1. 手写投递单(内存)│ ← 管理员手写草稿,断电就丢
│ (redo log buffer) │
└──────────┬───────────┘
│ 扫码上传
▼
┌──────────────────────┐
│ 2. 电子屏暂存(OS缓存)│ ← 显示在柜子屏幕上,但未存服务器
│ (OS Page Cache) │
└──────────┬───────────┘
│ 同步到云端
▼
┌──────────────────────┐
│ 3. 云端备份(磁盘) │ ← 真正持久化,断电也不丢
│ (ib_logfile0/1) │
└──────────────────────┘
各层说明:
- 第 1 层(手写草稿):事务生成的 redo 记录先暂存在内存,快但易失。
- 第 2 层(电子屏) :数据已传给操作系统,但仍在内存,服务器断电会丢。
- 第 3 层(云端备份):只有同步到磁盘文件,才算真正安全。
🔑 关键 :
"放进快递柜" ≠ "包裹安全" ,
只有"云端备份成功",才算万无一失。
四、刷盘策略:你选择哪种"投递保险"?
通过参数 innodb_flush_log_at_trx_commit,你可以选择快递柜的保险等级:
| 等级 | 行为 | 安全性 | 类比 |
|---|---|---|---|
| 1(推荐) | 每次投递都立即同步到云端 | ✅ 断电不丢 | 贵重物品,当场上传云端 |
| 2 | 投递后只显示在电子屏,由系统稍后上传 | ⚠️ 服务器宕机不丢,断电会丢 | 普通包裹,依赖柜子供电 |
| 0 | 每秒批量上传一次 | ❌ 最多丢 1 秒包裹 | 便宜货,定时上传 |
五、两阶段提交:快递柜 + 邮政系统的协同
当开启 binlog(用于主从复制),InnoDB 还要和 邮政系统(binlog) 协同,确保两边记录一致。
投递流程(两阶段提交):
- Prepare 阶段 :
- 管理员把包裹放进快递柜,并贴上 "待确认"标签(redo log 标记为 PREPARE)
- 通知邮政 :
- 同时把包裹信息登记到 邮政系统(写 binlog)
- Commit 阶段 :
- 确认邮政已记录 → 给快递柜包裹贴 "已发货"标签(redo log 标记为 COMMIT)
为什么需要两步?
- 防止"快递柜有记录,但邮政没登记" → 主从不一致
- 防止"邮政有记录,但快递柜丢了" → 主库数据异常
崩溃恢复时:
- 若包裹只有"待确认"标签:
- 查邮政系统:有记录 → 补贴"已发货";无记录 → 丢弃
- 保证快递柜与邮政系统最终一致
💡 这就是 MySQL 实现 Crash-Safe 主从复制 的秘密。
六、崩溃恢复:断电后如何"找回包裹"?
假设:
- 包裹已放进快递柜(redo log 已落盘)
- 但尚未整理入库(脏页未刷盘)
- 此时仓库断电
重启后:
- 管理员检查快递柜(扫描 redo log)
- 从 最后入库位置(checkpoint) 开始
- 把所有"已发货"但未入库的包裹 重新整理进仓库
- 仓库恢复到断电前的状态
✅ 用户无感知,数据零丢失。
七、总结:Redo Log 的核心价值
| 问题 | 答案 | 快递柜类比 |
|---|---|---|
| 为什么快? | 顺序写日志替代随机写数据页 | 投递快于入库 |
| 为什么安全? | WAL + fsync + 崩溃恢复 | 云端备份防丢 |
| 如何保一致? | 两阶段提交协调 binlog | 快递柜 + 邮政系统同步 |
| 生产怎么配? | flush_log=1 + sync_binlog=1 |
贵重物品当场上传 |
💡 记住 :
"用户看到成功" ≠ "包裹已入库" ,
"数据真正安全" = "快递柜已云端备份"。
理解 redo log,就是理解 MySQL 如何在 性能与可靠性之间取得精妙平衡。