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 reload或SELECT 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_delay、autovacuum_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_limit和autovacuum_vacuum_cost_delay是否与预期并发匹配——高并发配高 delay 等于白加 worker。 - [ ] 对关键大表检查是否有表级
autovacuum_vacuum_workers设置(如果未来版本支持表级并发控制),避免全局和局部配置冲突。 - [ ] 在测试环境先做一轮"白天 3 / 夜间 8"的切换演练,观察
pg_stat_progress_vacuum和系统 CPU/IO 变化。
PostgreSQL 18 这个改动看似只是拆了一个参数,但对生产运维的实际影响不小——autovacuum 终于可以从"建库时定死"变成"按负载动态调配"。如果你还在用旧版本靠重启调 worker 数量,升级到 18 后这会是第一批值得用起来的新特性。