一句话定义
预写日志(WAL,Write-Ahead Logging)规定「先写日志、后写数据页」:提交只需把顺序追加的 Redo 日志刷盘,数据页可延迟批量落盘;崩溃后用 Redo 重放已提交、用 Undo 回滚未提交,恢复出「恰好包含全部已提交事务」的状态。
为什么重要
- 它同时兑现原子性与持久性(kp-014 的 A 和 D),是数据库「断电不丢数据」承诺的全部秘密。
- 「commit 返回了数据却在哪」的疑问、组提交调优、主从半同步复制,全部以 WAL 机制为底座。
- 备份与 PITR(kp-031)就是「基础备份 + 重放 WAL 归档」,理解本篇才能设计可靠的容灾方案。
前置知识
kp-014(事务生命周期)、kp-019(页与缓冲池的存在)。
核心概念
- WAL 三原则:日志先于数据页落盘;日志顺序追加(顺序 IO);数据页可异步批量刷。
- Redo 日志:物理修改记录(「页 P 偏移 o 写入值 v」),保证已提交事务可重放。
- Undo 日志:逻辑反操作记录,回滚未提交事务并支撑 MVCC 版本链(kp-017)。
- LSN(Log Sequence Number):日志全局单调序号,页与日志间的一致性标尺。
- 检查点(Checkpoint):周期性把脏页刷盘并记录「该点之前的日志不再需要」。
- ARIES 三阶段:分析(Analysis,确定恢复范围)→ 重做(Redo)→ 回滚(Undo)。
原理与机制
提交点被精确定义为「该事务的 Redo 日志已 fsync 落盘」:此后数据页无论何时写盘(甚至一直不写盘)都不影响持久性,崩溃后恢复器从检查点扫描日志重放到末尾即可重建。这样把「每次提交的随机页写」变成「顺序日志追加 + 后台批量页写」,随机 IO 瓶颈被消除——这就是 WAL 的性能红利来源。组提交(Group Commit)进一步把并发事务的日志合并为一次 fsync,吞吐随并发近线性增长。ARIES 恢复:分析阶段从检查点出发找出崩溃时的活跃事务与脏页范围;Redo 阶段按 LSN 顺序重放所有已落日志(重复重放幂等,因 Redo 记「物理置位」);Undo 阶段逆序回滚未提交事务。InnoDB 的实现即 doublewrite + redo + undo 的变体,PostgreSQL 的 WAL 记录页级修改、配合 full_page_writes 防止「部分页写」撕裂。
实例或案例
一次提交的完整旅程:UPDATE account SET balance=900 WHERE id=1 → 改缓冲池中的页(此时页是脏的)→ 写 redo log buffer(含页号、偏移、新值)→ COMMIT 触发 redo fsync(组提交合并他人)→ 返回客户端成功。此刻磁盘上数据页可能还是 1000 的旧值。若此刻断电:重启后恢复器发现该 LSN 之后有已提交事务标记,重放 redo,页被写为 900;若事务只写了 redo 未到提交标记,则 Undo 回滚,余额保持 1000。两种结果都符合 ACID。
常见误区
- 认为
COMMIT返回 = 数据页已写盘:返回只保证日志落盘,数据页由后台按检查点节奏刷出。 - 把 redo 与 binlog 混为一谈:redo 是存储引擎层的崩溃恢复日志,binlog 是 Server 层的复制/归档日志,两套体系(InnoDB 有两阶段内提交保证二者一致)。
- 关闭日志「提速」:没有 WAL 就没有崩溃恢复与复制,等于用正确性换速度。
自测题
- 为什么 WAL 能把随机写变成顺序写? 答:提交只追加顺序日志文件,脏页的随机落盘被推迟到检查点批量执行,磁盘面对的是顺序追加流。
- 检查点越频繁越好吗? 答:不是。频繁检查点增加脏页刷写冲击(性能毛刺),过疏则恢复时间变长(要重放更多日志),需按 RTO 与负载折中。
公式或模型
提交延迟 ≈ 计算 + fsync(日志);组提交下单位日志 fsync 摊薄:每次提交均摊 fsync ≈ 1 / 并发组大小。恢复时间 RTO ≈ 自上个检查点以来的日志量 × 重放速率。
图示
直观类比
WAL 像会计的流水账:每笔交易先记进一本顺序流水账并签字画押(fsync),账本(数据页)可以事后慢慢誊抄;哪怕誊抄到一半打翻墨水(断电),只要流水账完好,重抄一遍就能还原所有已确认的交易,未确认的当没发生。
与其他知识点的关系
kp-014 的 A/D 靠它兑现;kp-017 的版本链载体是 Undo;kp-022 管理脏页刷盘节奏;kp-023 的复制流与 kp-031 的 PITR 都以日志流为原料。
延伸阅读
- Mohan 等《ARIES: A Transaction Recovery Method Supporting Fine-Granularity Locking...》(TODS 1992)
- 《MySQL 技术内幕:InnoDB 存储引擎》(姜承尧)日志章节