PostgreSQL Field Guide

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 indexescleaning 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();

它相当于手动 VACUUMPARALLEL 选项的 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 dbname

vacuumdb --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 评分

按我的负载调整 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

On this page