27.3 WAL、检查点与写入平滑
写事务的磁盘路径不是“把 table row 写到文件后提交”。
近似链路:
change shared buffer
-> generate WAL record
-> insert into WAL buffer
-> write/flush WAL according to commit policy
-> acknowledge commit
-> data page later written by backend/bgwriter/checkpointWAL 让 data page 可以延后写,却把 commit latency、checkpoint、archive、replication 和 crash recovery 连接成一个系统。调其中一项,必须看整条链。
27.3.1 WAL 生成、刷盘与提交延迟
write、flush 与 acknowledge
需要区分:
WAL generated
WAL copied/written to kernel
WAL flushed to durable storage
WAL sent to standby
WAL written/flushed/replayed on standby
client receives success不同 synchronous_commit level 改变 acknowledge 等待的阶段。它不是“开/关性能”:
on:本地 durable,并遵守 synchronous standby 配置;remote_apply:还等待 synchronous standby replay;remote_write:等待 standby 写入 OS;local:等待本地 flush,不等待 synchronous standby;off:允许本地 WAL 尚未 flush 就向 client 确认。
具体 durability 还受 synchronous_standby_names、synchronous_standby_slots、
standby availability 和 application route 影响。不要看到 on 就自动推导
multi-node zero-loss,也不要看到 off 就说“数据会损坏”;它改变的是 crash 时最近
成功确认 transaction 的 loss window。
fsync 与 full_page_writes 不是普通 candidate
fsync=off
full_page_writes=off可能显著提高 benchmark TPS,但会改变 crash safety。PostgreSQL 官方
WAL settings
明确说明,在可能发生 OS/硬件 crash 的系统中关闭相关保护可能导致不可恢复或静默
损坏。
教学 benchmark 若关闭它们,测的是另一个产品合同。
group commit
多个 backend 可以共享一次 flush:
backend A commits
backend B commits nearby
-> one WAL flush may make both durable因此:
- 单 client commit latency 不代表多 client;
- TPS 与 fsync count 不一定 1:1;
- storage fsync tail 比平均 throughput 更重要;
- batch/transaction boundary 改变 group opportunity。
commit_delay/commit_siblings 可以人为等待以扩大 group,但等待本身增加 latency,
只有在高并发、flush 昂贵且实验支持时才考虑。默认不是越大越好。
测 WAL composition
累计层:
SELECT
wal_records,
wal_fpi,
wal_bytes,
wal_buffers_full,
stats_reset
FROM pg_stat_wal;PostgreSQL 18 把 WAL I/O bytes/timing 纳入 pg_stat_io;不要照抄旧版本
pg_stat_wal.wal_write_time/wal_sync_time 查询:
SELECT
backend_type,
object,
context,
reads,
read_bytes,
writes,
write_bytes,
fsyncs,
write_time,
fsync_time
FROM pg_stat_io
WHERE object = 'wal'
ORDER BY backend_type, context;列与可用 timing 取决于 PostgreSQL major version 与
track_wal_io_timing。升级时先查 system view schema。
statement 层:
SELECT
queryid,
calls,
wal_records,
wal_fpi,
wal_bytes
FROM pg_stat_statements
ORDER BY wal_bytes DESC
LIMIT 20;不要公开 query text;用 queryid 对回内部 catalog。
分母要有业务语义
WAL bytes / mixed transaction
WAL bytes / write transaction
WAL bytes / order
WAL bytes / changed row不是同一个指标。
第 26 章 mixed workload 只有 20% place-order,约 158–196 WAL bytes/mixed tx。 不能把它写成“每个订单 196 bytes”。需要按成功 write operation 归一,并把 background activity 与 full-page image phase 作为误差。
WAL buffer full
wal_buffers_full 增加表示 WAL buffer 空间不足时 backend 被迫写 WAL,但不能只看到
counter 就把 wal_buffers 手工调大:
- 默认
-1会基于 shared buffers 自动选择; - upper bound 通常一个 WAL segment;
- 写入/flush device 可能才是限制;
- checkpoint/FPI 或大 transaction 可能改变 burst;
- counter 是累计 delta。
先对齐发生时间与 WAL generation/write/fsync。
commit latency 的证据链
client transaction latency
-> server wait event
-> WAL I/O timing/count
-> device fsync latency/queue
-> synchronous standby wait
-> network/replay如果 backend 等 SyncRep,加快本地 WAL device 未必改善;如果 server CPU 已饱和,
启用更重 WAL compression 可能反而恶化。
27.3.2 检查点频率、写突发与恢复时间
checkpoint 做什么
checkpoint 建立 recovery 起点,并把需要的 dirty buffer 写出,使 crash recovery 不必从无限久之前 replay WAL。它不是“定期 fsync 一次”这么简单。
触发:
time: checkpoint_timeout
WAL volume: max_wal_size soft threshold
manual/requested: CHECKPOINT or operational action
shutdown/recovery transitionsPostgreSQL 18 的证据:
SELECT *
FROM pg_stat_checkpointer;
SELECT *
FROM pg_stat_bgwriter;checkpointer 与 bgwriter 已分开统计。关注:
num_timed / num_requested / num_done
write_time / sync_time
buffers_written
backend writes/fsync
checkpoint warnings/log duration不要用旧版本列名硬编码跨版本 dashboard。
max_wal_size 是 soft limit
它不是:
pg_wal hard cap
archive backlog cap
slot retention cap
disk safety guarantee在 archive failure、replication slot、wal_keep_size、重负载等条件下,WAL 可超过
max_wal_size。把 disk provision 写成 max_wal_size + 10% 是错误模型。
太小的症状
requested/WAL-driven checkpoints frequent
checkpoint_warning
WAL FPI rate high
write/sync burst
backend writes increase
throughput/latency periodic sawtooth因为每次 checkpoint 后,某页第一次修改需要 full-page image(在保护开启时),过于 频繁会增加 WAL。
太大的代价
增加 max_wal_size/checkpoint_timeout 可能:
- 减少 checkpoint 频率;
- 降低 checkpoint-induced FPI;
- 让 dirty work 有更长时间平滑;
- 增加 crash recovery 要 replay 的 WAL;
- 增加
pg_walworking space; - 延后 dirty page write,扩大 failure/burst 状态;
- 改变 archive/replica catch-up。
优化点不是“checkpoint 越少越好”,而是满足:
foreground SLO
write smoothness
crash RTO
WAL disk headroom
archive/replica behaviorcheckpoint_completion_target
它控制 checkpoint 在 checkpoint interval 中用于完成的目标比例。默认较高是为了把 I/O 分散到大部分 interval。降低会让 checkpoint 更快完成:
higher instantaneous write rate
-> then longer quiet period通常不是想要的平滑。参考 Pigsty 沙箱为 0.95;改变它前要看 checkpoint progress、 device latency 与 SLO,而不是复制旧时代 0.7。
写入平滑不是平均值
画 time series:
WAL bytes/s
checkpoint begin/end
data write bytes/s
fsync latency
dirty buffers
backend writes
archive rate/gap
replica receive/replay gap
OLTP p95/p99平均 20MB/s 可能是:
持续 20MB/s或:
每 10 秒 200MB/s,其余为 0设备和 tail latency 面对的是后者。
恢复时间要实测
粗略:
$$ T_{\text{recovery}} \approx \frac{\text{WAL to replay}} {\text{effective replay throughput}} + \text{startup/finalization} $$
replay throughput 受:
WAL record mix
CPU
data/WAL storage
full-page images
compression/decompression
extension
prefetch
recovery conflicts影响。不能用 WAL device sequential bandwidth 代替。
checkpoint 调整 acceptance 应含 crash/restart 或 replica rebuild drill,而不是只跑 steady primary。
不要为 benchmark 强制 checkpoint
每次 run 前:
CHECKPOINT;会人为同步 phase:
- 一开始 full-page image 更多;
- dirty state 被清空;
- I/O burst 与 production natural phase 不同。
如果研究 checkpoint phase,应把 “immediately after checkpoint / mid-cycle” 作为显式 factor;否则让背景系统自然运行并记录 checkpoint。
27.3.3 压缩、全页写与归档代价
full-page image 的正确性作用
当 full_page_writes=on,checkpoint 后某页第一次被修改时,WAL 通常记录整页 image,
以防 torn page 让 recovery 无法还原。于是:
frequent checkpoint
-> more first modifications
-> more FPI
-> more WAL但关系受 working set 与 write locality 影响。修改同一小批 hot page 与随机修改巨大 dataset 的 FPI 比例不同。
wal_compression
PostgreSQL 18 支持的 method 取决于 build:
off
pglz
lz4
zstd它主要压缩 full-page image,不是压缩所有 WAL record。tradeoff:
less WAL bytes
vs
more primary compression CPU
more replay decompression CPU
different archive/network/storage load参考沙箱:
wal_compression = lz4
source = configuration file
context = superuser第 26 章 c8 已有 CPU pressure,却没有证明 WAL I/O 是 throughput limit。因此“开启更强 compression 减少 WAL”不符合当前 service center evidence。
compression 实验矩阵
至少比较:
| 维度 | 指标 |
|---|---|
| primary | TPS、p95、CPU、wal_bytes、wal_fpi |
| WAL device | write/fsync bytes/latency |
| network/archive | bytes/s、backlog、catch-up |
| replica | replay CPU、lag、recovery |
| backup/PITR | archive compatibility、restore time |
| failure | crash replay、promotion |
只看 wal_bytes 降低不能决定接受。
archive 与 slot 可能让 WAL 无界
WAL generated
-> pg_wal recycle/remove eligibility
depends on checkpoint
+ archive completion
+ replication consumers/slots
+ wal_keep_size
+ backup/recovery needs若 archive throughput $A$ 小于 WAL generation $W$:
$$ \text{backlog growth}
W-A $$
增加 max_wal_size 只推迟 disk full,不修复 consumer。
若 slot inactive:
SELECT
slot_name,
slot_type,
active,
restart_lsn,
confirmed_flush_lsn,
wal_status,
safe_wal_size
FROM pg_replication_slots;列随版本变化,先查当前 view。还要配置/验证 max_slot_wal_keep_size 等 guard,但 guard
触发可能使 consumer 无法继续,属于 recovery/runbook 决策。
min_wal_size 与 recycle
min_wal_size 让一定量旧 WAL segment 被 recycle 供未来使用,减少 burst 时反复创建/
删除。它不是最小 WAL generation,也不是 retention policy。
filesystem 类型可能影响:
wal_init_zero
wal_recycle尤其 CoW filesystem。但这些是 storage-specific candidate,应以文件系统、allocation 行为和 crash test 证明,不能把云盘经验搬到本地 SSD。
归档压缩与 WAL compression 是两层
wal_compression
compresses full-page images inside WAL records
archive/backup compression
compresses WAL files/backup objects during transport/storage二者可叠加,CPU 消耗发生在不同进程/节点/阶段。容量模型要测最终 archive object
bytes 和两侧 CPU,不要把 primary wal_bytes ratio 当作备份压缩率。
参数 ADR
WAL/checkpoint 变更应写:
symptom:
requested_checkpoints_per_hour: ...
wal_fpi_ratio: ...
hypothesis:
parameter: max_wal_size
mechanism: reduce WAL-driven checkpoint frequency
baseline:
checkpoint_interval: ...
wal_bytes_s: ...
crash_recovery_p95: ...
candidate:
value: ...
benefit_gate:
foreground_p95: ...
non_regression:
pg_wal_peak: ...
archive_gap: ...
replica_replay: ...
crash_recovery: ...
rollback:
value/source: ...
restart_or_reload: ...没有 recovery/retention 证据的 checkpoint 优化,只完成了一半。
上一节:内存预算 · 返回本章目录 · 下一节:规划器、并行与连接参数 · 查看全书目录 · 查看索引中心