autovacuum_work_mem:给自动清理工人单独发工资

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

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

预计阅读时间:7 分钟

PostgreSQL 的 autovacuum 在清理死元组时,需要一块内存来记录所有待清理的元组标识符(TID)。这块内存的上限由 autovacuum_work_mem 控制。默认值 -1,意思是"跟 maintenance_work_mem 一样"。乍一看省事,但一旦你把 maintenance_work_mem 调到几百 MB 甚至 1 GB 来加速手动 VACUUMCREATE INDEX,autovacuum 的每个 worker 就会照搬这个数字——多个 worker 同时开工时,内存开销可能远超预期。

这个参数的存在,就是为了把 autovacuum 的内存预算从 maintenance_work_mem 里拆出来,单独管控。

死元组追踪的内存代价

autovacuum worker 在扫描表时,会把发现的死元组 TID 收集到内存里。一个 TID 占 6 字节(4 字节块号 + 2 字节偏移量)。当收集的 TID 数量填满 autovacuum_work_mem 的配额,worker 就得停下来,先把当前批次清理掉,再回去继续扫描——这就是所谓的"多轮 vacuum"。

多轮 vacuum 的代价不小:每一轮都要重新扫描索引来删除对应的索引条目。如果一张表上有好几个索引,反复扫描的 I/O 成本会快速累积。

粗略估算一下:假设 autovacuum_work_mem 设为 128 MB,能容纳约 2240 万个 TID。一张频繁更新的表如果单次更新周期产生超过这个数量的死元组,autovacuum 就不得不分多轮处理。

为什么不能直接依赖 maintenance_work_mem

maintenance_work_mem 是给手动运维命令用的——VACUUM FULLCREATE INDEXALTER TABLE ADD CONSTRAINT 等。这些命令通常只有一个在跑,给大内存没问题。

autovacuum 则不同:autovacuum_max_workers 默认是 3,每个 worker 都会按 maintenance_work_mem 的值申请内存。如果你把 maintenance_work_mem 设成 1 GB,3 个 worker 同时干活就是 3 GB,再加上普通后端进程的 work_mem 开销,机器内存可能扛不住。

autovacuum_work_mem 单独设一个更保守的值,既能让手动运维享受大内存,又不会让后台清理进程悄悄把内存吃光。

参数上下文:sighup

autovacuum_work_mem 的上下文是 sighup,意味着修改后不需要重启 PostgreSQL,只需向 postmaster 进程发送 SIGHUP 信号——也就是执行一次配置重载:

-- 修改后重载配置,立即生效(不需要重启)
SELECT pg_reload_conf();

或者用命令行:

pg_ctl reload -D /var/lib/postgresql/data

这很实用:你可以在观察到 autovacuum 行为异常时,随时调整内存配额,不用等维护窗口重启数据库。

实操:观察与调整

第一步:看清当前配置

-- 查看 autovacuum_work_mem 的当前值和来源
SELECT name, setting, unit, source, boot_val
  FROM pg_settings
 WHERE name = 'autovacuum_work_mem';

-- 如果 setting 显示 -1,说明继承 maintenance_work_mem
SELECT name, setting, unit, source
  FROM pg_settings
 WHERE name IN ('autovacuum_work_mem', 'maintenance_work_mem');

第二步:估算你的表需要多大配额

-- 查看某张表的死元组数量(最近一次统计的信息)
SELECT relname,
       n_dead_tup,
       n_live_tup,
       round(n_dead_tup * 6.0 / 1024 / 1024, 2) AS dead_tup_mb
  FROM pg_stat_user_tables
 WHERE n_dead_tup > 0
 ORDER BY n_dead_tup DESC
 LIMIT 10;

dead_tup_mb 列告诉你:如果要把这些死元组的 TID 全部装进内存,至少需要多少 MB。把这个数字和你当前的 autovacuum_work_mem(或继承的 maintenance_work_mem)对比,就能判断是否会出现多轮 vacuum。

第三步:设置独立的 autovacuum_work_mem

假设你的 maintenance_work_mem 是 1 GB(适合手动 CREATE INDEX),但希望 autovacuum worker 每个只用 256 MB:

# 在 postgresql.conf 中添加或修改
echo "autovacuum_work_mem = 256MB" >> /var/lib/postgresql/data/postgresql.conf

# 重载配置
pg_ctl reload -D /var/lib/postgresql/data

或者用 ALTER SYSTEM(PostgreSQL 9.4+):

-- 设置 autovacuum 独立内存配额
ALTER SYSTEM SET autovacuum_work_mem = '256MB';
SELECT pg_reload_conf();

-- 验证生效
SELECT name, setting, unit, source
  FROM pg_settings
 WHERE name = 'autovacuum_work_mem';

第四步:确认 autovacuum 的实际内存消耗

# 观察 autovacuum 进程的 RSS(实际物理内存使用)
ps -o pid,rss,cmd -C postgres | grep autovacuum

设置前后对比这个数值,能直观看到调整效果。

调多少合适:几个决策因素

因素 方向
表上索引数量多 倾向给更大配额,减少多轮 vacuum 的索引重复扫描
autovacuum_max_workers 大(≥3) 倾向给更小配额,控制总内存开销
表更新频率极高、死元组量大 倾向给更大配额,争取一轮清完
机器内存紧张 倾向保守,宁可多轮 vacuum 也不能 OOM
手动运维需要大 maintenance_work_mem 必须拆开设置,不能让 autovacuum 继承

一个常见的起步值是 128–256 MB,然后根据 pg_stat_user_tables 里的死元组规模和 autovacuum 的实际运行时间(pg_stat_progress_vacuum)微调。

快速检查清单

  • [ ] autovacuum_work_mem 是否还在用默认 -1(继承 maintenance_work_mem)?
  • [ ] maintenance_work_mem 是否超过 256 MB?如果是,autovacuum 继承它很可能过高。
  • [ ] 用 pg_stat_user_tables 估算最大死元组表的 TID 内存需求。
  • [ ] 用 pg_stat_progress_vacuum 观察是否有表频繁进入多轮 vacuum。
  • [ ] 设置独立的 autovacuum_work_mem 后,用 ps 确认 worker 进程的 RSS 变化。
  • [ ] 调整后持续观察 autovacuum 的完成耗时是否改善。

把 autovacuum 的内存预算从 maintenance_work_mem 里拆出来,是 PostgreSQL 运维里一个低成本、高回报的调整。不需要重启,不需要改表结构,一条 ALTER SYSTEM 加一次重载就能生效。下次你调 maintenance_work_mem 的时候,记得顺便给 autovacuum 单独算一份账。


相关推荐