PostgreSQL 18:autovacuum 并发调优终于不用重启了

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

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

预计阅读时间:8 分钟

PostgreSQL 的 autovacuum 是保障表健康运转的核心机制,但长期以来有一个让人头疼的限制——autovacuum_max_workers 属于 postmaster 级参数,改它必须重启整个数据库。生产环境里谁敢随便重启?于是很多集群的 autovacuum 并发数从建库那天起就锁死不动了。PostgreSQL 18 把这块配置拆开,新增了 autovacuum_worker_slots,让你可以在线调整 autovacuum 的并发能力,不用再为一次调优付出停机代价。

旧参数的尴尬

autovacuum_max_workers 控制的是 autovacuum 启动器(launcher)最多能派发多少个 worker 进程。它被归类为 postmaster 参数,意味着修改后必须执行 pg_ctl restart 或等效操作才能生效。这在以下场景里尤其难受:

  • 大表突然写入暴增,vacuum 压力飙升,需要临时加 worker 抢救——但重启不可接受。
  • 夜间批量导入完成后想收缩 worker 释放 CPU——同样受制于重启。
  • 多租户集群各租户负载周期不同,需要动态调配——完全不可能。

结果就是,大多数人建库时设一个"看起来够用"的值(比如 3 或 6),然后再也不动它。这个值要么平时浪费资源,要么高峰时不够用。

新参数 autovacuum_worker_slots 做了什么

PostgreSQL 18 把 autovacuum 的并发控制拆成两层:

  • autovacuum_max_workers 仍然是 postmaster 级参数,定义系统启动时创建的 worker 进程池大小。改它仍需重启。但它现在更像一个"上限容量"——你预留多少个 worker 进程槽位。
  • autovacuum_worker_slots 是新增的 SIGHUP 级参数,定义 launcher 实际最多能同时派发多少个 worker。改它只需 pg_ctl reloadSELECT pg_reload_conf(),秒级生效。

两者的关系很简单:autovacuum_worker_slots 的值不能超过 autovacuum_max_workers。你可以把 max_workers 设成一个较大的上限(比如 10),然后通过 worker_slots 在线调节实际并发(比如白天 3、夜间 8),无需重启。

实操:从配置到验证

下面是一个完整的操作流程,展示如何利用新参数做在线调优。

第一步:设定进程池上限(需重启)

postgresql.conf 中把进程池容量设够,给后续动态调节留出空间:

# postgresql.conf

# 进程池上限——启动时预留的 worker 进程数量,改这个需要重启
autovacuum_max_workers = 10

# 实际并发——launcher 当前最多派发几个 worker,改这个只需 reload
autovacuum_worker_slots = 3

修改 autovacuum_max_workers 后需要重启:

pg_ctl restart -D /var/lib/postgresql/18/data

第二步:在线调节并发(无需重启)

白天负载平稳,保持 3 个 worker 就够了。夜间批量写入结束后,临时加大并发清理:

# 夜间加大并发——修改 postgresql.conf 或用 ALTER SYSTEM
sed -i 's/^autovacuum_worker_slots = 3/autovacuum_worker_slots = 8/' \
    /var/lib/postgresql/18/data/postgresql.conf

# reload 秒级生效
pg_ctl reload -D /var/lib/postgresql/18/data

也可以用 SQL 完成同样的操作,适合从运维平台远程下发:

-- 夜间:加大并发
ALTER SYSTEM SET autovacuum_worker_slots = 8;
SELECT pg_reload_conf();

-- 查看当前生效值
SHOW autovacuum_worker_slots;
-- 8

-- 白天:收缩并发
ALTER SYSTEM SET autovacuum_worker_slots = 3;
SELECT pg_reload_conf();

第三步:验证 worker 活动情况

调完参数后,确认 worker 确实按新并发数工作:

-- 查看当前活跃的 autovacuum worker 进程
SELECT pid,
       datname,
       relid,
       phase,
       heap_blks_vacuumed,
       heap_blks_total
FROM pg_stat_progress_vacuum;

-- 查看各表的 vacuum 统计,判断压力是否缓解
SELECT relname,
       n_dead_tup,
       last_autovacuum,
       autovacuum_count
FROM pg_stat_all_tables
WHERE n_dead_tup > 10000
ORDER BY n_dead_tup DESC
LIMIT 10;

调优思路与边界

有了在线调节能力,日常运维可以更灵活,但也要注意几个边界:

进程池不要设过大。 autovacuum_max_workers 决定启动时预留多少个后台进程,每个进程都占内存和连接槽。设成 20 但平时只用 3,等于白白浪费资源。建议设成你预估的峰值并发再加 2-3 的余量即可。

worker_slots 调大不等于清理变快。 autovacuum 的 I/O 和 CPU 受 autovacuum_vacuum_cost_delayautovacuum_vacuum_cost_limit 等参数约束。加 worker 只是让更多表同时被清理,单表清理速度不变。如果你的瓶颈是单张大表 vacuum 太慢,应该调 cost_limit 或对该表单独设参数,而不是堆 worker 数量。

不要忘了 cost_delay 的配合。 加大并发时,如果 cost_delay 仍然很高,多个 worker 会各自慢悠悠地干活,总吞吐提升有限。夜间加大 worker_slots 的同时,可以考虑同步降低 cost_delay:

-- 夜间激进模式:更多 worker + 更低延迟
ALTER SYSTEM SET autovacuum_worker_slots = 8;
ALTER SYSTEM SET autovacuum_vacuum_cost_delay = 2;  -- 默认 2ms,白天可能设更高
ALTER SYSTEM SET autovacuum_vacuum_cost_limit = 2000; -- 默认 200,夜间可以放大
SELECT pg_reload_conf();

reload 不是瞬间完成派发。 pg_reload_conf() 让 launcher 读到新值,但已有 worker 不会被打断。新并发上限在下一轮 launcher 周期(默认 1 秒检查一次)才会体现。如果当前有 3 个 worker 正在跑,你把 slots 调到 8,launcher 会在后续周期逐步派发更多 worker,直到达到 8 或没有更多表需要清理。

上线前的检查清单

如果你打算在 PostgreSQL 18 上启用这套动态调优,上线前确认以下事项:

  • [ ] autovacuum_max_workers 设为合理的峰值上限(不是越大越好),改完需一次重启。
  • [ ] autovacuum_worker_slots 初始值设为日常所需并发,后续可在线调节。
  • [ ] 确认 worker_slots ≤ max_workers,否则 reload 时 PostgreSQL 会报错并拒绝生效。
  • [ ] 检查 autovacuum_vacuum_cost_limitautovacuum_vacuum_cost_delay 是否与预期并发匹配——高并发配高 delay 等于白加 worker。
  • [ ] 对关键大表检查是否有表级 autovacuum_vacuum_workers 设置(如果未来版本支持表级并发控制),避免全局和局部配置冲突。
  • [ ] 在测试环境先做一轮"白天 3 / 夜间 8"的切换演练,观察 pg_stat_progress_vacuum 和系统 CPU/IO 变化。

PostgreSQL 18 这个改动看似只是拆了一个参数,但对生产运维的实际影响不小——autovacuum 终于可以从"建库时定死"变成"按负载动态调配"。如果你还在用旧版本靠重启调 worker 数量,升级到 18 后这会是第一批值得用起来的新特性。


相关推荐