PostgreSQL bgwriter 的核心杠杆:lru_maxpages 与 lru_multiplier 调优实战

2026-05-17 29 预计阅读时间:1 分钟
来源:postgr.es AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:9 分钟

PostgreSQL 的后台写入器(background writer)每隔一轮醒来,要决定写多少脏页。bgwriter_delay 控制它多久醒一次,但真正决定"写什么、写多少"的杠杆,是 bgwriter_lru_maxpagesbgwriter_lru_multiplier 这两个参数。理解它们的算法,才能把缓冲区的清洗节奏调到不拖后端查询、也不白白刷盘的位置。

bgwriter 每轮的决策算法

把算法压缩成一句话:本轮写多少页 = min(近期新分配缓冲区数 × multiplier, maxpages)

展开来看:

  1. bgwriter 每隔 bgwriter_delay 毫秒醒来一次。
  2. 它记录上一轮结束时共享缓冲区中新分配(newly allocated)的页面数量——这些是后端进程因为查询需要而从缓冲池里拿走的页。
  3. 用这个数量乘以 bgwriter_lru_multiplier(默认 2.0),得到一个"预期需要清洗的页数"。
  4. 再和 bgwriter_lru_maxpages(默认 100)做上限裁剪——不管乘出来多大,一轮最多写 100 页。
  5. 按这个数量从缓冲区 LRU 链表尾部挑脏页写回磁盘,写完后继续睡眠。

为什么用"近期分配数"做基数?因为后端进程分配新缓冲页的速度,直接反映了系统对干净缓冲区的需求强度。乘以 multiplier 是给 bgwriter 一个"提前量"——不是只补已经用掉的,而是多准备一些干净页,让下一波查询来了不用等。

lru_maxpages:一轮能写多少的上限

bgwriter_lru_maxpages 是硬天花板。即使 multiplier 算出来要写 500 页,maxpages 设成 100 就只写 100。它的作用:

  • 防止 bgwriter 一轮占用过多 I/O 带宽,和后端查询的读写抢磁盘。
  • 设为 0 等于关闭 bgwriter 的 LRU 写入功能——脏页清洗完全交给后端进程自己做,或者靠 checkpoint 来批量冲刷。

什么时候该调大?当你的监控显示后端进程频繁执行同步写(pg_stat_bgwriter 里的 buffers_backend 偏高),说明 bgwriter 供不上干净页,后端被迫自己刷脏。这时可以逐步加大 maxpages,让 bgwriter 多承担一些。

什么时候该调小甚至归零?如果存储 I/O 已经是瓶颈,bgwriter 的后台刷盘和后端查询的 I/O 互相踩脚,反而让整体吞吐下降。把 maxpages 调低,让更多脏页积攒到 checkpoint 时集中写,用 checkpoint 的节奏来控盘——前提是 checkpoint 间隔要够长,否则会更惨。

lru_multiplier:提前量的倍数

bgwriter_lru_multiplier(默认 2.0)决定 bgwriter 的"激进程度"。乘数越大,每轮清洗越超前,干净缓冲区储备越充裕,但 I/O 开销也越大。

一个直观的理解:

multiplier 行为特征
1.0 只补本轮用掉的,不提前准备——后端下次分配时可能仍要等
2.0(默认) 补本轮用掉的两倍,留一半给下一轮——大多数场景的平衡点
≥3.0 大幅超前清洗,适合缓冲区压力大、查询不能容忍同步写的场景

注意:multiplier 的效果受 maxpages 上限约束。如果 maxpages 很低,multiplier 再大也发挥不出来——算出来的数被截断了。

用 pg_stat_bgwriter 验证调优效果

调这两个参数不能靠猜,要看 pg_stat_bgwriter 的数据。关键字段:

字段 含义
buffers_clean bgwriter 通过 LRU 算法写回的页数——这是 bgwriter 主动干的活
buffers_backend 后端进程被迫同步写回的页数——这个数高说明 bgwriter 不够给力
buffers_alloc 后端进程分配的缓冲区总数——这是 multiplier 计算的基数
buffers_checkpoint checkpoint 写回的页数

理想状态:buffers_clean 占大头,buffers_backend 很小,buffers_checkpoint 在合理范围内。如果 buffers_backend 远超 buffers_clean,bgwriter 在偷懒,该加大 maxpages 或 multiplier。

下面是一个可以直接跑的监控查询,帮你判断当前 bgwriter 是否在有效工作:

-- 查看 bgwriter 工作分布,判断是否需要调优
SELECT
  buffers_clean,
  buffers_backend,
  buffers_alloc,
  buffers_checkpoint,
  round(buffers_clean::numeric /
        NULLIF(buffers_clean + buffers_backend + buffers_checkpoint, 0)
        * 100, 1) AS clean_pct,
  round(buffers_backend::numeric /
        NULLIF(buffers_clean + buffers_backend + buffers_checkpoint, 0)
        * 100, 1) AS backend_pct,
  round(buffers_checkpoint::numeric /
        NULLIF(buffers_clean + buffers_backend + buffers_checkpoint, 0)
        * 100, 1) AS checkpoint_pct
FROM pg_stat_bgwriter;

跑一次记下数值,调参数后过几分钟再跑一次,对比 backend_pct 是否下降——这是最直接的验证方式。

调优实操:从默认值出发逐步调整

默认配置(maxpages=100, multiplier=2.0, delay=200ms)对很多中等工作量是够用的,但遇到高写入负载或大缓冲池时往往偏保守。下面是一个调优流程示例:

# 第一步:看当前 bgwriter 统计
psql -c "SELECT buffers_clean, buffers_backend, buffers_alloc FROM pg_stat_bgwriter;"

# 第二步:如果 buffers_backend 占比过高,先加大 maxpages
psql -c "ALTER SYSTEM SET bgwriter_lru_maxpages = 200;"
psql -c "SELECT pg_reload_conf();"

# 第三步:等 5-10 分钟,再看统计对比
sleep 600
psql -c "SELECT buffers_clean, buffers_backend, buffers_alloc FROM pg_stat_bgwriter;"

# 第四步:如果 backend 写入仍高,同时加大 multiplier
psql -c "ALTER SYSTEM SET bgwriter_lru_multiplier = 3.0;"
psql -c "SELECT pg_reload_conf();"

# 第五步:再观察一轮,确认 backend_pct 下降且整体 I/O 未恶化
sleep 600
psql -c "SELECT buffers_clean, buffers_backend, buffers_alloc FROM pg_stat_bgwriter;"

几个要点:

  • 每次只动一个参数,观察后再动下一个,否则无法判断哪个变化起了作用。
  • 关注系统级 I/O 指标iostatsar),确保 bgwriter 多写没有把磁盘利用率推到后端查询受不了的程度。
  • maxpages 和 multiplier 是联动的——如果 maxpages 太小,加大 multiplier 没意义;反过来,maxpages 很大但 multiplier 只有 1.0,bgwriter 也写不了多少页。

边界与取舍

bgwriter 的本质是用后台异步 I/O 替代后端同步 I/O。但磁盘带宽是固定的,bgwriter 多写一分,后端能用的 I/O 就少一分。调优的核心取舍:

  • I/O 不紧张、后端延迟敏感:加大 maxpages 和 multiplier,让 bgwriter 尽量把脏页提前洗掉,后端分配缓冲区时几乎不用等同步写。
  • I/O 已经是瓶颈:反过来,降低 maxpages(甚至设 0),让脏页攒到 checkpoint 集中写。代价是后端偶尔要同步刷脏,查询会有毛刺延迟。但整体吞吐可能反而更好,因为减少了随机写和后端争抢。
  • 缓冲池很大(几 GB 以上):bgwriter 的 LRU 清洗更重要,因为 checkpoint 间隔内积攒的脏页量巨大,如果全靠 checkpoint 冲刷会造成严重的 I/O 暴风。这时 bgwriter 应该更激进。

最后一条检查清单,调完参数后逐项确认:

  • buffers_backend 占总写入的比例是否降到 10% 以下?
  • ☑ 系统磁盘 I/O 利用率是否仍在可接受范围(<70%)?
  • ☑ 查询的 p99 延迟是否没有因 I/O 争抢而恶化?
  • ☑ checkpoint 期间的 IOPS 峰值是否没有变得更剧烈?

bgwriter 的三个参数是一套联动系统——delay 决定频率,maxpages 和 multiplier 决定每轮力度。把 pg_stat_bgwriter 的数据当指南针,一步步调,比照搬任何"最佳配置"都靠谱。


相关推荐