Redo / Undo / WAL(为什么 MySQL 写比读复杂)
🔥 一句话先破题
MySQL 的所有写操作 = 改内存 + 写日志(两个日志) → 最后才慢慢刷磁盘数据页。
原因是:
直接改磁盘太慢,且不安全,必须通过日志来“骗过磁盘的慢”。
这就是 WAL(Write-Ahead Logging)的本质。
🍔 先上总图(最通俗的写流程,助你先有大局观)
你执行:
UPDATE user SET age=20 WHERE id=1;
InnoDB 实际上做了 4 件事:
- 生成 Undo Log:记录“怎么回滚”
- 改内存中的数据页(Buffer Pool)
- 生成 Redo Log:记录“怎么重做”
- 先把 Redo Log 落盘(刷到 redo log file)
这是 WAL:先写日志,再写数据
最终数据页还是在内存里,过一段时间才被刷回磁盘(flush)。
为什么要搞这么多环节?
因为磁盘太慢了,数据库只能耍聪明。
🟦 1. Undo Log:用来“穿越时间”的日志
Undo Log 是 MySQL 自己的“时光倒流机器”。
用途:
- 事务回滚(roll back)
- MVCC 读取旧版本(快照读)
你更新 age=20,那么 Undo Log 会记录:
原来的 age=18
原来的 row trx_id=xxx
也就是:
我怎么把这一行恢复到更新前?
Undo Log 存在 Undo Segment 里,属于“逻辑日志”。
🟥 2. Redo Log:保障 Crash-Safe 的日志(比 Undo 更底层)
Redo Log 不是为了回滚,而是为了 宕机恢复(Crash Recovery)。
Redo Log 记录的是:
我对某个页做了哪些物理操作(偏移量 + 改了什么值)
也就是:
数据库重启后,我可以照着 redo log 把内存里的修改重新“补”到磁盘页上。
Redo Log 是物理日志,且是循环写的(固定大小文件,如 2GB)。
🧊 Undo + Redo 的关系指南(最容易混)
你可以这样记:
| 目标 | 用哪个 |
|---|---|
| 想回到过去(回滚) | Undo Log |
| 想补上未来(崩溃恢复) | Redo Log |
| 想恢复旧版本记录给 MVCC | Undo Log |
Undo 是 “回滚我自己”
Redo 是 “重做我做过的事”
两个日志互补,缺一不可。
🧠 3. WAL:Write-Ahead Logging(写之前先记账)
核心原则:
数据还没写盘可以,但 redo log 必须先写盘。
为什么?
因为如果只改了内存,MySQL 崩了,那数据就丢了。
所以写流程如下:
BEGIN;
改内存页 → 写 redo buffer → 刷 redo log(fsync) → COMMIT
数据页本身可以晚点刷盘(这叫异步刷盘),
但 redo log 必须先落盘,才能保证“崩了不丢”。
这就是 WAL 的灵魂。
🎬 4. 整个写查询的动画版(让你脑补一遍)
🔔 步骤 1:生成 Undo Log
为了能回滚 → 先记录旧值
Undo Log: {id=1, old age=18}
🧱 步骤 2:修改内存页(Buffer Pool)
页还没刷到磁盘,只是在 RAM 里改了一份。
📝 步骤 3:生成 Redo Log(InnoDB Log Buffer)
记录“我改了哪个页、偏移量、改了什么字段”。
🔥 步骤 4:刷 Redo Log 到磁盘
这是 commit 时最关键的一步
也是 MySQL 写性能瓶颈的来源(fsync)
保证崩机也能恢复
🕒 步骤 5:后台慢慢把数据页刷盘(异步)
最终数据会被刷到磁盘的 .ibd 文件。
注意:这个步骤跟 commit 无关,不需要马上做。
🧨 为什么 MySQL 写比读复杂?(重点解释)
读:
- 看 Buffer Pool
- 没有 → 读一页
- 完事
写:
需要:
✔ 1. 生成 Undo Log
为了回滚 + MVCC 历史版本
✔ 2. 改 Buffer Pool
脏页(dirty page)形成
✔ 3. 生成 Redo Log
为了 crash-safe
✔ 4. WAL:Redo 先刷盘(昂贵的 fsync)
这是必须的
✔ 5. 后台还要刷脏页(checkpoint)
由后台线程专门处理
✔ 6. Redo Log 是循环写 → 还要配合 checkpoint
避免覆盖
写操作包含:
- 两份日志(Undo/Redo)
- 两次写(redo write、data page write)
- 事务管理
- MVCC 版本链维护
- 缓冲池管理
写要做的事情至少是读取的 5〜10 倍。
🧩 再讲一个你一定会懂的比喻:
想象你写论文:
读论文 = 打开文件 → 读一小段 → 做笔记 → 关掉
写论文 =
- 做备份(Undo)
- 打草稿(改内存页)
- 写修改日志(Redo)
- 把日志保存到本地(WAL:fsync)
- 最后再慢慢把最终版本保存(Checkpoint)
写比读累得多这一点,就很像 MySQL。
🎯 最终总结一张图(你面试用它吊打别人)
+---------------------+
| Client SQL |
+---------+-----------+
|
v
+------------------------------+
| 1. Undo Log(逻辑) |
+------------------------------+
|
v
+------------------------------+
| 2. 修改 Buffer Pool 页(内存)|
+------------------------------+
|
v
+------------------------------+
| 3. Redo Log(物理日志) |
+------------------------------+
|
v
+------------------------------+
| 4. WAL:先写 redo 文件 |
| (fsync, 最关键最慢) |
+------------------------------+
|
事务提交成功
|
v
+------------------------------+
| 5. 后台刷脏页到磁盘(慢) |
+------------------------------+
浙公网安备 33010602011771号