← [ BACK_TO_NOTES ]

BodySense 数据库第 16 课:WAL、Checkpoint、Crash Recovery 与 Durability

从 PostgreSQL WAL 与 checkpoint 理解事务持久性、write-ahead 原则、crash recovery 与 fsync,并把 ACID 的 Durability 连接到真实存储恢复机制。

#course #growing #backend #database #postgresql #bodysense

[!info] related notes

BodySense 数据库第 16 课:WAL、Checkpoint、Crash Recovery 与 Durability

[!abstract] 本课目标 理解 COMMIT 为什么不是“把每张表的数据页全部立即写回磁盘”,以及数据库如何在 crash 后恢复到一致状态。

从零建立概念:Durability 为什么不能靠“把所有数据页立刻写盘”

先回到 ACID 的 D:

Transaction COMMIT 成功后,即使 PostgreSQL 进程或机器随后 crash,已提交结果也应该能恢复。

最直觉的做法似乎是:

UPDATE 10 tables
↓
把所有修改过的 data pages 立刻 fsync 到 disk
↓
然后告诉客户端 COMMIT success

这当然有机会做到 durable,但性能很差。

原因:

一个 transaction 可能修改很多分散 pages
→ lots of random writes
→ lots of fsync / storage latency

PostgreSQL 使用 WAL 来解耦:

Commit durability
≠
all data pages already written

WAL 是什么

WAL = Write-Ahead Log,预写日志。

名字最重要的词是:Ahead。

核心规则:

描述某个数据页修改的 WAL record,必须在对应修改过的数据页被认为 durable 之前先写到持久存储。

粗略:

UPDATE row
↓
modify page in memory
↓
generate WAL record
↓
flush required WAL
↓
COMMIT can be durable
↓ later
dirty data page can be written to table file

为什么这有用?

因为如果 crash 时:

WAL durable
but data page not yet written

重启后可以:

replay WAL
→ redo change
→ bring data files forward

Log Record 不是业务 Event

WAL 很容易和:

RuntimeEvent
Kafka event
Audit log

混淆。

WAL 的 ownership 是 PostgreSQL storage engine:

physical/logical changes needed for database recovery/replication

它不是给前端阅读的业务 event stream。

例如 BodySense:

runtime_events row
= domain/runtime durable event

WAL record for inserting that row
= PostgreSQL internal recovery data

两个都“像日志”,语义完全不同。

LSN:WAL 中的位置

LSN = Log Sequence Number。

可以先把它想成:

WAL 字节流中的一个位置坐标。

例如:

0/16B6C50

LSN 可用于:

replication progress
backup/recovery position
WAL flush position
diagnostics

不是 transaction ID,也不是业务 sequence。

WAL Buffer → OS → Durable Storage

“写 WAL”也有层次:

PostgreSQL generates WAL
→ WAL buffers / write
→ OS cache
→ fsync / flush
→ storage guarantees persistence

所以真正 Durability 不只是 PostgreSQL 一层。

还依赖:

fsync settings
synchronous_commit
OS
filesystem
virtualization
cloud disk
hardware cache

错误硬件/虚拟化若虚假确认 flush,也会破坏数据库对 durability 的假设。

fsync 是什么

应用 write() 返回,不一定代表 bytes 已经安全到断电后仍存在的介质。

fsync 一类操作要求 OS/存储层把必要写入推进到持久状态。

代价是:

durability stronger
↔
storage flush latency

WAL 的价值之一,是让每次 transaction 不必 fsync 大量随机 data pages,而主要保证必要 WAL 持久化。

Group Commit:多个 Transaction 可以共享一次昂贵 Flush

假设短时间有:

T1 commit
T2 commit
T3 commit

数据库可以把多个 transaction 的 WAL flush 合并。

于是一次 storage flush 可以让多个 transaction 达成 durability。

这叫 group commit 的基本思想。

所以:

WAL 不只是正确性机制,也是高吞吐 transaction 系统的性能基础。

Dirty Page 是什么

Page 从 disk 读进 shared buffer 后被修改:

in-memory page
≠
disk file current content

它就是 dirty page。

Commit 并不要求所有 dirty page 立刻落盘。

后续 background writer/checkpoint 等机制会逐步把它们写回。

Checkpoint 到底是什么

如果 data pages 永远不落盘,只积累 WAL:

crash recovery
→ 可能要从很久以前重放海量 WAL

Checkpoint 周期性推进一个恢复基点:

ensure relevant dirty pages reach disk
record checkpoint information
→ recovery can start from a newer point

Checkpoint 太频繁

lots of forced page writes
I/O spikes
more full-page-write overhead around checkpoints

Checkpoint 太稀疏

more dirty state / WAL to recover
longer crash recovery

所以仍然是 trade-off。

Crash Recovery:REDO 的基本思想

PostgreSQL 意外停止后:

data files
可能停在某个较旧/部分写入状态

WAL
包含已经 durable 的变更记录

启动时恢复过程:

find recovery starting point
→ read WAL forward
→ redo changes that data pages still need
→ reach consistent end
→ accept normal traffic

PostgreSQL 官方将此称为 roll-forward / REDO recovery。

WAL 与 RDB/AOF 的对照

Redis AOF:

Redis dataset persistence mechanism

PostgreSQL WAL:

transaction/storage recovery foundation

共同点:

log before/for recovery

但不要把实现和语义一一等同。

PostgreSQL 的 WAL 还深度参与:

MVCC/transaction system
physical page recovery
replication
PITR

WAL 为什么还能做 Replication 和 PITR

如果:

Primary
→ continuously produces WAL

Replica 可以:

receive WAL
→ replay changes
→ keep physical copy near primary

Backup 也可以:

base backup
+
archived WAL
→ replay to target point

所以:

Crash Recovery
Replication
PITR

共享同一个底层 WAL 思维。

synchronous_commit 为什么是一个业务取舍

在某些配置下可以让 transaction 更早返回,把部分 WAL flush 延后。

这样:

latency ↓

但 crash 时:

最近已向客户端返回 success 的 transaction
可能存在额外丢失窗口

是否可接受取决于数据类型。

例如:

disposable analytics
vs
payment / health durable record

不能用同一个“性能更快”理由决定。

1. Write-Ahead Logging

核心原则:

先把描述修改的 WAL record 持久化
→ 数据 page 可以稍后刷盘

这样即使数据 page 还没落盘,只要必要 WAL 已 durable,重启时就能重放恢复。

2. 为什么不每次 Commit 都刷所有数据页

随机写很多数据 page 成本高。WAL 把大量修改先变成相对顺序的日志写,再由后台逐步写 dirty pages。

3. Checkpoint

Checkpoint 让系统建立一个恢复基点,并推动 dirty page 刷盘。Checkpoint 太频繁会增加 I/O;太稀疏则 crash recovery 可能需要重放更多 WAL。

4. Commit 与 fsync

Durability 依赖存储栈真正把必要数据持久化。synchronous_commit、fsync、磁盘/云盘语义都影响“服务告诉客户端 commit 成功”时的保障。

5. WAL 还有哪些用途

PostgreSQL WAL 同时是:

crash recovery foundation
→ physical replication stream
→ PITR base + WAL archive

因此恢复、复制和备份不是互相独立的知识岛。

Prediction

[!question]

  1. Transaction commit 后,所有被修改 table page 是否必须已经写回数据文件?
  2. 为什么 WAL 必须“先于”对应数据页持久化?
  3. 只保留最新 base backup、不保留后续 WAL,能做到任意时间点恢复吗?
  4. Checkpoint 越频繁越安全吗、越好吗?
  5. 应用返回 200 后主机突然断电,Durability 由哪些层共同决定?

[!success]- Answer / 解析 1. 不需要。 关键是事务所需 WAL 已满足 durability 条件,dirty data page 可以之后刷盘。

2. 否则 crash 时可能出现数据页包含数据库无法通过日志解释/恢复的修改,破坏恢复协议。

3. 不能。 PITR 需要 base backup 加目标时间点之前连续可用的 WAL archive。

4. 不一定。 更频繁 checkpoint 可能制造大量 I/O;恢复时间与运行期写压力需要平衡。

5. PostgreSQL commit 配置、WAL flush/fsync、OS、虚拟化/云盘持久化语义和硬件共同构成。

Lab

在隔离开发数据库查看 pg_current_wal_lsn()、checkpoint/WAL 统计;做一批写入前后比较 WAL 增量。阅读 PostgreSQL 日志中的 checkpoint 信息。

L4 Gate

能从 COMMIT → WAL durable → dirty page later → crash → replay 解释 PostgreSQL Durability,不再把 commit 理解成“直接保存表文件”。

创建于 2026/9/18 更新于 2026/9/18
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本