bigywater.tech
PostgreSQL · 3

autovacuum 什么时候触发:门槛是 50 + 0.2 × 行数,更新 2000 行不触发、累计 2100 行触发,大表为什么一直不清理

“autovacuum 到底什么时候跑、为什么大表一直不清理、日志怎么看” 是 PostgreSQL 运维里最常被问的问题。这篇在本机自己起一个 PostgreSQL 16.15,只把 autovacuum_naptime 调成 1s,其余阈值保持默认,用 pg_stat_user_tables 和 log_autovacuum_min_duration = 0 的日志逐个验证。

2050
1 万行的表 autovacuum 的触发门槛(50 + 0.2 × 行数);更新 2000 行不触发,累计 2100 行触发
20050
10 万行的表默认门槛;单表设 scale_factor = 0 后 5000 行死行 1.0 s 内被清掉
5000
老事务开着时 autovacuum 照跑但清不掉的死行数(dead but not yet removable)

默认门槛

实测 · 本机执行输出
  reltuples = 10000 → vacuum 门槛 = 50 + 0.2 × 10000 = 2050 ,analyze 门槛 = 50 + 0.1 × 10000 = 1050
  建好之后的基线:n_dead_tup、autovacuum_count、autoanalyze_count = (0, 0, 0)
  更新 2000 行、等 6 秒之后:n_dead_tup = 2000,autovacuum_count = 0,autoanalyze_count = 1
  再更新 100 行(累计 2100)之后:autovacuum_count 在 0.3 s 内变成 1,n_dead_tup = 0

vacuum 门槛 = 50 + 0.2 × 行数;analyze 门槛 = 50 + 0.1 × 行数,analyze 先到。

日志

实测 · 本机执行输出
      LOG:  automatic analyze of table "postgres.public.t"
      LOG:  automatic vacuum of table "postgres.public.t": index scans: 1
      pages: 0 removed, 66 remain, 24 scanned (36.36% of total)
      tuples: 1950 removed, 8692 remain, 0 are dead but not yet removable
      index "t_pkey": pages: 36 in total, 0 newly deleted, 0 currently deleted, 0 reusable
      index "t_v_idx": pages: 35 in total, 5 newly deleted, 5 currently deleted, 0 reusable

log_autovacuum_min_duration = 0 会把每次自动清理的页数、清掉的行数、各索引的页数记进日志。

大表

实测 · 本机执行输出
  默认参数:更新 5000 行、等 6 秒之后:n_dead_tup = 5000,autovacuum_count = 0(门槛是 50 + 0.2 × 100000 = 20050)
  给 big 设了 scale_factor = 0、threshold = 1000 之后:autovacuum_count 在 1.0 s 内变成 1,n_dead_tup = 0

门槛随行数线性增长;给单张表设 autovacuum_vacuum_scale_factor = 0 加一个固定 threshold,才能及时清理。

自动 ANALYZE

实测 · 本机执行输出
  手动 ANALYZE 之后:reltuples = 10000,autoanalyze_count = 0
  插入 1500 行之后 1.0 s:reltuples = 11500,autoanalyze_count = 1

插入也算修改行数,超过门槛后统计信息自动更新,reltuples 跟上。

长事务

实测 · 本机执行输出
  老事务还开着:autovacuum_count = 2,n_dead_tup = 5000
      日志:tuples: 0 removed, 15000 remain, 5000 are dead but not yet removable
  老事务结束后约 0.8 s:autovacuum_count = 3,n_dead_tup = 0

autovacuum 照跑但回收不掉,日志写 dead but not yet removable;事务结束后才清掉。

分步演示

autovacuum 实测
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / PostgreSQL 16.15(Homebrew);每个实验自己 initdb 一个数据目录,端口 9261 起,只监听 127.0.0.1,不碰本机已有的 5432 上的实例;命令行工具统一用 LC_ALL=C 取英文原文,路径换成 ;计时用 time.perf_counter 或 EXPLAIN ANALYZE 的 Execution Time,数字每次运行会有小幅抖动;实验结束后整个数据目录删除。autovacuum_naptime 调成 1s(默认 1min),其余阈值是默认值;为了让统计信息及时上报,DML 都放在一次性连接里执行并关闭(空闲连接的统计最多晚 10 秒才上报);每张表建好后手动 VACUUM ANALYZE,并先关掉再恢复这张表的 autovacuum_enabled,保证看到的 autovacuum_count 都来自实验动作
    • 没有验证:autovacuum_vacuum_cost_delay / cost_limit 对清理速度的限制;autovacuum_max_workers 不够时的排队;防止事务号回卷的 aggressive vacuum 触发条件(autovacuum_freeze_max_age);autovacuum_vacuum_insert_threshold 对只插入的表的影响;PostgreSQL 其他大版本的默认值;真实业务负载下的清理节奏
    京ICP备2026067502号-1京公网安备11010502063487号