PostgreSQL 19 并行 autovacuum 与评分调度
PostgreSQL 19 让 autovacuum 并行处理索引并按可调评分排定表的优先级——相关 GUC、存储参数与 pg_stat_autovacuum_scores 视图
PostgreSQL 19 仍处于 Beta
截至 2026-08,PostgreSQL 19 尚未正式发布。下文参数名、视图列与默认值以 PostgreSQL 19 文档为准;用于生产前请对照正式 release notes 复核。
PostgreSQL 18 及之前:串行的 autovacuum
到 PostgreSQL 18 为止,autovacuum 有两个长期存在的限制:
- 一个 autovacuum worker 一次只处理一张表,且 vacuuming indexes 和 cleaning up indexes 阶段逐个串行处理索引。一张有十几个索引的宽表可能独占 worker 数小时,其他表只能排队。
- 在单个 database 内,worker 大致按
pg_class目录顺序处理候选表。一张逼近事务 ID wraparound 的表,与一张刚过 analyze 阈值的表,没有形式化的优先级差别。
手动 VACUUM 从 PostgreSQL 13 起就支持 PARALLEL 并行处理索引,但 autovacuum 一直用不上。给大表补上这个缺口只能靠人工定时跑手动 vacuum——基础机制见 autovacuum 与表膨胀。
并行索引处理:autovacuum_max_parallel_workers
autovacuum_max_parallel_workers 设置单个 autovacuum worker 在 index vacuuming 和 index cleanup 阶段最多可招募的并行 worker 数。默认 0(禁用),需要显式开启:
ALTER SYSTEM SET autovacuum_max_parallel_workers = 4;
SELECT pg_reload_conf();它相当于手动 VACUUM 的 PARALLEL 选项的 autovacuum 版本。实际 worker 数还受 max_parallel_workers 限制——那是与并行查询共享的总池子。
表级存储参数可以给单表设上限,避免某张热点表吃光并行预算:
ALTER TABLE app.events SET (autovacuum_parallel_workers = 2);约束条件(见官方文档 24.1.7 节 Parallel Vacuum):
- 索引只有大于
min_parallel_index_scan_size才能参与并行,且每个索引最多一个 worker——所以一张表至少要有两个合格索引才会启动并行 worker。 - 计算出的 worker 数不保证全部到位,实际可能更少甚至为零。
- 并行 worker 沿用 leader autovacuum worker 的 cost delay 参数,既有的节流模型仍然生效。
评分调度系统
在单个 database 内,autovacuum worker 现在先构建候选表列表,再按评分排序,而不是按目录顺序处理。一张表的评分是五个分量评分的最大值(见 24.1.6.1 节 Autovacuum Prioritization):
- 事务 ID 年龄——
age(relfrozenxid)相对autovacuum_freeze_max_age的比例;超过vacuum_failsafe_age后急剧上升。权重:autovacuum_freeze_score_weight。 - multixact ID 年龄——
relminmxid相对autovacuum_multixact_freeze_max_age的比例;超过vacuum_multixact_failsafe_age或 multixact member 超过约 20 亿条后急剧上升。权重:autovacuum_multixact_freeze_score_weight。 - vacuum——更新/删除的元组数相对 vacuum 阈值。权重:
autovacuum_vacuum_score_weight。 - vacuum insert——插入元组数相对 insert 阈值。权重:
autovacuum_vacuum_insert_score_weight。 - analyze——变更元组数相对 analyze 阈值。权重:
autovacuum_analyze_score_weight。
五个权重默认都是 1.0(一视同仁),用 pg_reload_conf() reload 即可生效。文档里有两个细节值得注意:
- freeze 权重调到 1.0 以上不只是放大评分——分量开始激进爬升的年龄会除以该权重,即 freeze 压力会更早被视为紧急。
- 把五个权重全部设为
0.0,调度策略退回 19 之前的目录顺序。
database 级的选择是另一层逻辑:launcher 仍然优先处理有 wraparound 风险的 database,其次是最久未处理的。
用 pg_stat_autovacuum_scores 观测
新增的 pg_stat_autovacuum_scores 视图展示当前 database 里每张表的实时评分,把"autovacuum 为什么不理这张表"从猜源码变成一次查询:
SELECT relid::regclass AS relation,
round(score::numeric, 1) AS score,
round(xid_score::numeric, 1) AS xid_score,
round(vacuum_score::numeric, 1) AS vacuum_score,
round(vacuum_insert_score::numeric, 1) AS insert_score,
round(analyze_score::numeric, 1) AS analyze_score,
do_vacuum, do_analyze, for_wraparound
FROM pg_stat_autovacuum_scores
ORDER BY score DESC
LIMIT 20;score 是五个 *_score 分量的最大值;do_vacuum / do_analyze 表示该表当前是否满足触发条件,for_wraparound 标记反 wraparound 压力。文档的一个提醒:视图用当前会话可见的信息计算评分,可能与 autovacuum worker 构建列表时看到的不完全一致——它是排障工具,不是处理顺序的保证。调权重前后各查一次,确认排序确实按预期变化——例如让 dead tuple 回收优先于统计刷新:
ALTER SYSTEM SET autovacuum_vacuum_score_weight = 2.0;
ALTER SYSTEM SET autovacuum_analyze_score_weight = 0.5;
SELECT pg_reload_conf();建议把这个视图和 pg_stat_progress_vacuum 一起纳入 监控与日志 中的日常巡检。
对比 PG18 的 vacuumdb --jobs
PostgreSQL 18 时代应对维护缓慢的经典办法是手动并行:
vacuumdb --jobs=4 --analyze dbnamevacuumdb --jobs 起多个连接、每个处理不同的表——这是跨表并行,时间窗口和调度都由你自己负责。它无法加速单张大表的索引阶段(除非你自己跑 VACUUM (PARALLEL n)),也改变不了 autovacuum 内部的处理顺序。
PostgreSQL 19 补的是另一条轴:单表索引阶段内的并行,加上感知紧急程度的排序,两者都是自动的。定时跑 vacuumdb 对可预测的批量窗口、整库 FREEZE 仍然有用,二者并不互斥。
什么时候不要开
- I/O 已饱和的系统。 并行索引 vacuum 会成倍增加并发 I/O 流。cost limit 仍然共享,但对延迟敏感、存储已打满负载的系统应小步开启,同时盯
pg_stat_io和查询延迟。 - 小库。 所有索引都小于
min_parallel_index_scan_size时,没有任何索引能参与并行——开了也没有效果。 - worker 预算紧张。 vacuum 并行 worker 从
max_parallel_workers里出,与并行查询共用。小实例上两边都调高可能挤占查询并行。 - 多数表只有一两个索引。 每个索引一个 worker,只有一个合格索引的表永远走不了并行。
和一切 autovacuum 调优一样:一次只改一个参数,用 pg_stat_autovacuum_scores 和 autovacuum 日志验证效果,而不是凭感觉。PostgreSQL 19 的更多变化见版本总览。
AI 提示词:调整 autovacuum 评分
帮我调整 PostgreSQL 19 的 autovacuum 评分。 1. 这是 SELECT relid::regclass, score, xid_score, mxid_score, vacuum_score, vacuum_insert_score, analyze_score, do_vacuum, do_analyze, for_wraparound FROM pg_stat_autovacuum_scores ORDER BY score DESC LIMIT 30; 的输出:(粘贴在这里) 2. 我的负载特征:(描述,例如 append-only 事件表 + 高频更新的队列表 + 夜间批量删除) 3. 请: - 解释排名靠前的表分别是哪个分量主导评分、为什么 - 给出 autovacuum_freeze_score_weight / autovacuum_vacuum_score_weight / autovacuum_analyze_score_weight 的具体建议值(注意:freeze 权重超过 1.0 还会拉低激进爬升的起始年龄) - 指出哪些表应该用表级存储参数而不是全局权重 - 如果有 for_wraparound 的表被饿着,明确警告我 4. 给出 pg_reload_conf() 之后验证效果的方法。
Last updated on