在PostgreSQL的进程模型中,bgwriter(background writer)是一个容易被忽视但对性能影响很大的后台进程。它的职责是在系统空闲时提前把共享缓冲区中的脏页写出到磁盘,从而减少后端进程在需要淘汰缓冲页时被迫自己执行IO的概率。控制这个进程行为的参数有好几个,其中最核心的就是bgwriter_delay和bgwriter_lru_maxpages,前者决定它多久醒一次,后者决定它一次最多干多少活。理解这两个参数的协作机制,是做好PostgreSQL内存与IO调优的基础。

bgwriter的工作机制与两个参数的关系
要理解参数的含义,先要看bgwriter的工作循环。这个进程启动后会不断重复一个简单的过程:根据缓冲区的使用情况,找出最近最少被访问的脏页,把它们写出到磁盘,然后休眠一段时间,醒来继续。bgwriter_delay控制的就是这个休眠时长,单位是毫秒,默认值200ms。也就是说,默认情况下bgwriter每秒钟大约醒来5次。
bgwriter_lru_maxpages则限制了bgwriter在单个循环周期内最多能写出多少个页面,默认值是100。一个页面通常是8KB,默认配置下单次循环最多写800KB的数据。如果在一个周期内需要写出的脏页数量超过了这个上限,bgwriter会提前结束本轮写出,把剩余的工作留给后续周期。
这两个参数是配合工作的。当脏页产出速度较慢时,bgwriter每轮实际写出的页面数会低于maxpages,此时PostgreSQL会动态缩短实际的休眠时间,让bgwriter更勤快地跑;当脏页产出速度很快、每轮都触及maxpages上限时,说明刷脏能力已经被参数卡住,多余的脏页只能堆积在缓冲区里。堆积到一定程度,后端进程在分配新缓冲页时会找不到足够干净的候选页,只能自己去写脏页,这就是所谓的直接IO抢占前台查询资源的现象。
此外还有一个配套参数bgwriter_lru_multiplier(默认2.0),bgwriter会根据最近几轮的平均需求乘以这个系数来预估本轮要写的页面数,但无论如何都不会超过bgwriter_lru_maxpages这个硬上限。所以maxpages实际上决定了bgwriter的理论吞吐天花板,而delay决定了响应节奏。
如何判断当前配置是否存在问题
调优不能凭感觉,PostgreSQL提供了pg_stat_bgwriter视图来观察bgwriter的实际表现,重点看三个字段:buffers_backend表示后端进程被迫自己写出的缓冲区数量,buffers_clean表示bgwriter写出的数量,buffers_checkpoint表示checkpoint写出的数量。
-- 查看 bgwriter 运行统计
SELECT buffers_clean, maxwritten_clean, buffers_backend,
buffers_backend_fsync, checkpoints_req, checkpoints_timed
FROM pg_stat_bgwriter;
-- 重置统计信息,方便观察调整后的效果
SELECT pg_stat_reset_shared('bgwriter');
其中maxwritten_clean字段特别值得注意,它统计的是因为达到bgwriter_lru_maxpages上限而提前终止写出循环的次数。如果这个数字持续快速增长,说明maxpages设置得太小,bgwriter的刷脏能力跟不上脏页产生的速度。如果buffers_backend的增速明显高于buffers_clean,则说明大量脏页的写出工作被推给了后端进程,前台会话的响应时间会因此抖动,这是最需要避免的情况。
一个常见的健康标准是:buffers_backend占比很低,buffers_clean承担主要的日常刷脏工作,maxwritten_clean增长缓慢甚至不增长。可以通过下面这种查询估算各渠道写出量的比例:
SELECT buffers_clean, maxwritten_clean, buffers_backend,
round(100.0 * buffers_backend /
NULLIF(buffers_clean + buffers_backend + buffers_checkpoint, 0), 2)
AS backend_pct
FROM pg_stat_bgwriter;
如果backend_pct长期超过10%到20%,就值得考虑调整参数了。需要注意观察窗口要足够长,最好覆盖业务高峰期,否则样本没有代表性。
不同场景下的参数调优建议
调整的基本思路是:先降bgwriter_delay让刷脏更及时,再升bgwriter_lru_maxpages提高单轮吞吐上限,然后观察pg_stat_bgwriter的变化。对于写入压力较大的OLTP系统,一个常见的起始配置是把delay降到100ms甚至50ms,maxpages提到1000左右。在SSD存储环境下,IO延迟很低,delay设为20ms到50ms也不会造成负担;而机械盘环境就要谨慎,过小的delay会让磁盘频繁处于小批量写入状态,反而降低整体吞吐。
调整的幅度建议循序渐进,每次改动一个参数,观察一两个业务高峰周期后再决定下一步。下面是一个写入密集型库的参考配置:
# postgresql.conf 中的相关配置 bgwriter_delay = 50ms # 缩短休眠,提高刷脏频率 bgwriter_lru_maxpages = 1000 # 单轮最多写出 1000 页(约 8MB) bgwriter_lru_multiplier = 4.0 # 更积极地预估写出需求 shared_buffers = 8GB
这些参数都可以在会话级别之外直接修改并重新加载,属于 sighup 类型,执行SELECT pg_reload_conf();即可生效,不需要重启数据库,这使得在线调优非常方便。
还要注意bgwriter不是万能的,它只负责LRU淘汰路径上的刷脏,大量脏页的最终落盘仍然由checkpoint机制完成。如果checkpoint间隔设置得过短,脏页大头都被checkpoint写掉了,bgwriter再怎么调也没有意义。所以完整的IO调优需要把checkpoint_timeout、checkpoint_completion_target和bgwriter参数放在一起统筹考虑。对于以读为主、写入很少的系统,保持默认值通常就够了,过度调优反而会增加不必要的后台IO开销。
最后总结一下两个参数的分工:bgwriter_delay决定勤快程度,bgwriter_lru_maxpages决定单次干活上限。判断问题时盯住maxwritten_clean和buffers_backend这两个指标,调整后用pg_stat_bgwriter验证效果,让日常刷脏尽量由bgwriter在后台默默完成,把前台进程从IO等待中解放出来,这才是参数调优的最终目标。
PostgreSQL bgwriter_delay bgwriter_lru_maxpages修改时间:2026-09-08 07:42:43