wal_buffers太小会导致wal写放大,因高并发下频繁触发walbufferwritelock等待并提前刷出未满页,增加i/o次数;需与max_wal_size协同调优,避免单独调整引发新问题。

wal_buffers 太小会导致 WAL 写放大
PostgreSQL 在事务提交时,会先把 WAL 日志写入内存缓冲区 wal_buffers,等刷盘或检查点触发时再批量落盘。如果这个值太小(比如默认的 16MB),高并发写入下容易频繁触发 WALBufferWriteLock 等待,甚至被迫提前刷出未满的页——这会把一次大块写拆成多次小写,显著增加 I/O 次数。
实操建议:
- 对 OLTP 场景,
wal_buffers至少设为shared_buffers的 1/32,但不低于 16MB;若shared_buffers是 2GB,可设为 64MB - 不要盲目设到几百 MB:过大会占用大量共享内存,且不会加速单次刷盘,反而可能拖慢 checkpoint 时的同步开销
- 修改后必须重启实例才生效(不是 reload)
checkpoint_segments 已被废弃,该看 max_wal_size
9.5 之后 checkpoint_segments 就没了,继续在配置里写它不会报错但完全无效。真正控制 WAL 文件生成节奏的是 max_wal_size 和 min_wal_size。它决定 PostgreSQL 在两次 checkpoint 之间最多允许积累多少 WAL 数据——设得太小,就会频繁触发 checkpoint,导致后台进程狂刷脏页 + WAL 落盘,CPU 和 I/O 双高。
实操建议:
- 观察
pg_stat_bgwriter.checkpoints_timed和checkpoints_req:如果后者占比长期 > 10%,说明max_wal_size太小,WAL 积压逼迫系统“被动 checkpoint” - OLTP 场景下,
max_wal_size建议设为物理内存的 1%~2%,但不低于 1GB;例如 64GB 内存,可设为 2GB -
min_wal_size设为max_wal_size的 1/4~1/2 即可,避免空闲期 WAL 文件反复创建销毁
wal_buffers 和 max_wal_size 不是独立调优项
这两个参数实际是协同影响 WAL 写路径的吞吐与稳定性:wal_buffers 影响“写入端”的缓冲效率,max_wal_size 影响“刷盘端”的节奏和压力。单独调大一个,另一方没跟上,效果会打折扣甚至引发新问题。
常见错误现象:
- 调大了
wal_buffers却没调max_wal_size→ WAL 缓冲积压更快,checkpoint 更频繁,bgwriter压力不降反升 - 只调大
max_wal_size但wal_buffers还是 16MB → 高并发下 WAL 写入锁争抢依旧严重,TPS 上不去 - 两者都调大后没监控
pg_stat_replication的write_lag和flush_lag→ 主从延迟悄然拉大,尤其在备库 I/O 较弱时
别忽略 wal_writer_delay 和 synchronous_commit
wal_buffers 和 max_wal_size 是骨架,但 WAL 写行为还受两个运行时机制调控:wal_writer_delay 控制 WAL writer 进程刷缓冲区的频率;synchronous_commit 决定事务是否必须等 WAL 落盘才返回成功。它们不改配置文件,却直接影响你看到的写延迟和吞吐。
实操建议:
-
wal_writer_delay默认 200ms,对高吞吐写入可降到 50~100ms,减少缓冲区堆积;但太低(如 10ms)会让 writer 进程 CPU 占用飙升 - 若业务能容忍短时间主从不一致,把
synchronous_commit = off可大幅降低单事务延迟,但要注意崩溃可能丢失最多 1 个 WAL segment 的数据 - 调整这两项无需重启,
SET LOCAL或修改postgresql.conf后pg_reload_conf()即可
最常被忽略的是:wal_buffers 大小会影响每个 WAL record 的对齐方式,而某些 SSD 对非对齐写特别敏感;max_wal_size 设置不当会直接让 checkpoint 成为 I/O 瓶颈。调参前务必用 pg_stat_bgwriter 和 pg_stat_database 看真实 WAL 生成速率和 checkpoint 频率,而不是凭经验拍脑袋。











