0) 先看一个“没有 WAL 会怎样”的场景
假设 Dream Log 正在执行一条 UPDATE。数据库先在内存修改数据,然后准备把对应的 8KB 数据页写回磁盘。就在写盘过程中突然断电:可能只写了 2KB,剩余 6KB 没写完,形成“部分写入”。重启后这个数据页可能不一致,严重时会影响实例恢复。
WAL(Write-Ahead Logging,预写式日志)就是为了解决这个矛盾:既要提交快,又要崩溃后可恢复。
1) WAL 的核心铁律:先写日志,再让数据页落盘
规则:任何数据页写入磁盘之前,描述该变更的 WAL 记录必须先安全落盘。
- 客户端发出
UPDATE,数据库先修改内存中的 Buffer Cache(脏页产生)。
- 生成 WAL 记录(例如:事务100把 Page 42 的第 7 行改为“醒了”)。
- 把 WAL 追加写入 WAL 文件,并在提交路径上完成刷盘确认(fsync)。
- WAL 落盘成功后,事务即可返回“提交成功”。
- 真正的 8KB 数据页可由后台进程稍后异步刷盘。
2) WAL 解决了什么问题(3 大作用)
A. 崩溃恢复(Crash Recovery)
宕机后内存全部丢失,数据页可能还没来得及落盘。重启时 PostgreSQL 会从最近 Checkpoint 往后重放 WAL(Redo),把“已经提交但尚未写入数据页”的变更补回去,恢复到一致状态。
B. 性能优化:把随机写变成顺序写
直接写数据页通常是随机 I/O,成本高;而 WAL 是连续追加写(顺序 I/O),吞吐和延迟都更稳定。所以提交路径优先保证 WAL 快速落盘,接口响应更快。
C. 复制与时间点恢复(PITR)
- 主从复制:从库持续接收并重放主库 WAL,即可与主库保持一致。
- PITR:“一份基础备份 + 一段时间内的 WAL”,可恢复到指定时间点(秒级)。
3) Checkpoint:为什么 WAL 不会无限增长
Checkpoint 会定期把脏页批量推进到数据文件,并在 WAL 中写入检查点记录。这样崩溃恢复只需从最近 Checkpoint 开始,不必从很久以前回放;同时更早的 WAL 也具备回收/复用条件。
4) 一张图看懂 WAL