
值班人员看到autovacuum worker持续活跃就判断清理正常。可订单表每分钟制造 50 万个 dead tupleVACUUM 每分钟只消化 20 万个任务没停债务却以每分钟 30 万个增长。Autovacuum 是否有效不能看进程是否存在要看可回收量、触发等待、排队时间和清理吞吐能否压住 dead tuple 的净增长。先把“在跑”拆成四个问题是否达到触发阈值 ↓ 是否拿到 worker 开始执行 ↓ 旧快照是否允许删除旧版本 ↓ 扫描与清理吞吐是否超过生成速度 ↓ dead tuple 债务是否持续下降任何一层为否都可能出现 autovacuum 有进程、表却继续增长。一个 worker 正在处理表 A也不能证明表 B 没在排队扫描到 100% 也不能证明被旧快照保护的 tuple 已被删除。可复现实验让生成速度持续高于回收速度在独立测试库运行DROPTABLEIFEXISTSorder_state;CREATETABLEorder_state(order_idbigintPRIMARYKEY,statustextNOTNULL,notetextNOTNULL);INSERTINTOorder_stateSELECTg,created,repeat(x,200)FROMgenerate_series(1,1000000)ASg;VACUUM(ANALYZE)order_state;会话 A 持续制造旧版本可手工重复执行数轮UPDATEorder_stateSETstatusCASEstatusWHENcreatedTHENpaidELSEcreatedEND;会话 B 每隔固定时间采样SELECTclock_timestamp()ASsampled_at,n_live_tup,n_dead_tup,n_tup_upd,n_tup_hot_upd,last_autovacuum,autovacuum_countFROMpg_stat_user_tablesWHERErelidorder_state::regclass;会话 C 查看正在进行的清理SELECTpid,relid::regclassASrelation,phase,heap_blks_scanned,heap_blks_total,heap_blks_vacuumed,index_vacuum_count,dead_tuple_bytes,max_dead_tuple_bytes,num_dead_item_ids,delay_timeFROMpg_stat_progress_vacuumWHERErelidorder_state::regclass;PostgreSQL 18 的进度视图已经按“死元组数据字节”和“dead item identifier 数量”暴露本轮索引清理缓存而不是旧版本中的 tuple 计数字段dead_tuple_bytes / max_dead_tuple_bytes接近 1说明本轮收集区接近容量上限可能需要再次进入索引清理循环index_vacuum_count 1说明一次 heap 扫描期间已执行多轮索引 vacuum常见原因是 dead tuple 很多或维护内存不足delay_time只有启用track_cost_delay_timing时才有意义为 0 不能直接证明 worker 没被限速heap_blks_scanned heap_blks_total包含因 Visibility Map 而跳过的块只表示扫描进度完成不表示每块都被物理读取或所有旧版本都已清除。n_dead_tup是估算统计刷新也有延迟。实验要看多个时间点的方向不把单点差值当精确速度。若 UPDATE 停止后债务下降说明清理能追上静止目标若持续写入时净斜率为正说明当前维护能力无法覆盖峰值。这个实验能证明生成与清理存在吞吐竞争不能单独证明瓶颈来自 I/O、cost delay、worker 不足还是旧快照后续证据用于区分。触发阈值大表为什么可能等太久常规 UPDATE/DELETE 触发条件可近似理解为vacuum threshold vacuum scale factor × table tuplesPostgreSQL 18 还提供autovacuum_vacuum_max_threshold为上述计算结果设置上限。默认上限是一亿条不代表所有大表都应该容忍一亿次变更后才清理。查看表级覆盖项SELECTrelname,reloptionsFROMpg_classWHEREoidorder_state::regclass;SELECTname,setting,unit,sourceFROMpg_settingsWHEREnameIN(autovacuum_vacuum_threshold,autovacuum_vacuum_scale_factor,autovacuum_vacuum_max_threshold,autovacuum_naptime);针对热表的参数必须从业务允许的最大债务反推而不是复制固定答案ALTERTABLEorder_stateSET(autovacuum_vacuum_threshold5000,autovacuum_vacuum_scale_factor0.01,autovacuum_vacuum_max_threshold500000,autovacuum_analyze_scale_factor0.02);这组数值只是实验起点。阈值降低会让任务更早、更频繁减少单次债务但也可能增加扫描和索引维护频率。验证必须同时看 dead tuple 斜率、I/O、查询尾延迟和每次 autovacuum 时长。纯 INSERT 表也需要 VACUUM 来设置可见性、冻结并维护空间触发使用 insert threshold 与 insert scale factor不能只盯 UPDATE/DELETE 公式。Worker有空位、能启动、够并发是三件事PostgreSQL 18 将可预留的 worker slot 与实际允许并发数分开SELECTname,setting,contextFROMpg_settingsWHEREnameIN(autovacuum_worker_slots,autovacuum_max_workers,autovacuum_naptime);autovacuum_worker_slots预留后台进程槽只能启动时设置autovacuum_max_workers控制同时运行的 worker 数autovacuum_max_workers大于 slots 不会产生额外效果launcher 按数据库轮询多个大表可能等待空闲 worker。只增加 worker 可能让多个任务同时争抢磁盘和缓存反而抬高业务延迟。先确认是排队而非单 worker 受限再用小步调整验证总清理吞吐。Cost delay跑得慢可能是主动让路Autovacuum 默认使用 cost-based delay。累计页访问成本达到 limit 后worker 会短暂睡眠目标是降低维护对业务 I/O 的冲击。SELECTname,setting,unit,sourceFROMpg_settingsWHEREnameIN(autovacuum_vacuum_cost_delay,autovacuum_vacuum_cost_limit,vacuum_cost_page_hit,vacuum_cost_page_miss,vacuum_cost_page_dirty);全局 cost limit 会在并行运行的 autovacuum workers 间按比例分配。把 delay 一刀切为 0 可能让维护更快也可能把膨胀事故转成 I/O 饱和事故。安全做法是先为单张问题表设置 reloptions小流量观察ALTERTABLEorder_stateSET(autovacuum_vacuum_cost_delay1,autovacuum_vacuum_cost_limit1000);数值不是推荐值。变更前记录 p95/p99、磁盘延迟、WAL、复制延迟和清理周期达到业务延迟或 I/O 停止阈值时恢复原 reloptions。ALTER TABLE ... RESET (...)只能恢复参数来源不能撤销已产生的 I/O。最隐蔽的情况扫完了但没有版本可删旧 snapshot、prepared transaction、复制槽或 standby feedback 可能保持清理 horizon。此时提高 worker 和 I/O 配额不会创造可回收版本。SELECTpid,usename,application_name,state,xact_start,backend_xmin,wait_event_type,wait_eventFROMpg_stat_activityWHERExact_startISNOTNULLORDERBYxact_start;SELECTgid,prepared,owner,databaseFROMpg_prepared_xacts;SELECTslot_name,slot_type,active,xmin,catalog_xmin,restart_lsn,inactive_sinceFROMpg_replication_slots;这些查询只读但可能暴露 SQL 和应用标识生产展示要控制权限。发现保留者后先联系业务所有者确认重试、恢复和 CDC 重新快照能力终止会话、提交 prepared transaction 或删除 slot 都不是默认诊断动作。HOT 与索引决定债务生成成本SELECTn_tup_upd,n_tup_hot_upd,round(100.0*n_tup_hot_upd/NULLIF(n_tup_upd,0),2)AShot_pctFROMpg_stat_user_tablesWHERErelidorder_state::regclass;HOT 比例低可能因为更新列出现在索引中或页面没有空间容纳新版本。减少无价值索引、避免无意义 UPDATE、合理评估 fillfactor可以降低索引写放大但 HOT 仍会生成新 heap tuple不能替代 VACUUM。不要忘记防回卷VACUUM 还负责冻结 XID/MXID。即使关闭普通 autovacuum系统仍会为防止 transaction ID wraparound 启动任务。接近 failsafe 时VACUUM 会取消 cost delay并可能跳过非必要索引清理把正确性置于性能之前。SELECTdatname,age(datfrozenxid),mxid_age(datminmxid)FROMpg_databaseORDERBYage(datfrozenxid)DESC;SELECToid::regclass,age(relfrozenxid),mxid_age(relminmxid)FROMpg_classWHERErelkindIN(r,m)ORDERBYage(relfrozenxid)DESCLIMIT20;空间膨胀与 XID 年龄是两条相关但不同的风险线。不能为了降低业务抖动而长期取消防回卷任务。生产处置顺序以固定时间窗采样 dead tuple、更新量、任务时长和表尺寸确认净债务。查当前任务阶段、worker 使用量、I/O 与锁等待区分排队和慢扫。查旧事务、prepared xact、slot 与反馈确认版本是否可回收。先在单表降低触发阈值仍追不上再评估单 worker 配额和并发数。修复长事务、无效索引、无意义更新和连接池事务泄漏等生成根因。同时验收 dead tuple 斜率、业务延迟、WAL、复制和 XID 年龄。最小止血可以是暂停非必要批量 UPDATE、限制作业并发或对单表执行一次普通VACUUM。它们只能降低当前增速只要长期生成速度仍大于清理速度债务会回来。这个证据能证明什么证据能证明不能证明有 autovacuum worker集群存在正在执行的自动清理目标表正在清、清得掉或追得上heap_blks_scanned totalheap 扫描阶段完成所有 dead tuple 都已删除index_vacuum_count 1本轮发生多次索引清理循环原因一定只是维护内存不足n_dead_tup连续上升估算债务趋势恶化精确 dead tuple 数及唯一根因降阈值后债务下降更早触发对该负载有效全局都应使用同一阈值增加 worker 后更快之前存在并发容量约束I/O 与业务尾延迟仍安全面试表达主线Autovacuum 是否有效看 dead tuple 的净斜率不看进程。依次验证阈值是否及时、worker 是否排队、OldestXmin 是否允许回收、cost/I/O 吞吐是否追上生成量再看 HOT 与索引写放大参数优先按热表局部调同时守住 XID/MXID freeze 年龄。实验清理DROPTABLEIFEXISTSorder_state;官方资料PostgreSQL 18Automatic VacuumingPostgreSQL 18Vacuum ConfigurationPostgreSQL 18VACUUM Progress ReportingPostgreSQL 18Cumulative StatisticsPostgreSQL 18.6 源码标签 REL_18_6