1 作用
1、移除死元组
- 删除死元组并对每个页面的活元组进行碎片整理(页内重排)。
- 删除指向死元组的索引元组。
2、冷冻老的Txid
- 必要时冻结老元组的Txid
- 更新冻结的与系统目录(pg_database和pg_class)相关的txid
- 如有可能,移除clog中不必要的部分
3、其他
- 更新已处理表的FSM和VM。
- 更新几个统计数据(pg_stat_all_tables等)。
2 分类
2.1 普通vacuum
(1) 从指定的表中获取每个表。
(2) 获取表的ShareUpdateExclusiveLock锁。此锁允许读取其他事务。
(3) 扫描所有页面以获取所有死元组,必要时冻结旧元组。
(4) 如果存在,则移除指向相应死元组的索引元组。
(5) 对表的每一页执行以下步骤(6)和(7)。
(6) 移除死元组并重新分配页面中的活元组。
(7) 更新目标表的相应FSM和VM。
(8) 如果最后一页没有元组,则截断最后一页。
(9) 更新与目标表的真空处理相关的统计信息和系统目录。
(10) 更新与真空处理相关的统计数据和系统目录。
(11) 如果可能的话,删除不必要的文件和clog的页面

清理过程会参考vm文件进行可见性判断,跳过不包含死元组的页,从而加速清理过程。
- VM在9.6版中得到了增强,以提高冻结处理的效率
- 新的VM显示页面可见性和关于在每个页面中元组是否冻结的信息

2.2 深度vacuum

Vacuum full:创建了新的数据文件,因此relfilenode 会改变,期间会阻塞表上的一切访问。
3 冻结过程
冻结极限txid
freezeLimit_txid=(OldestXmin−vacuum_freeze_min_age)
启动冻结处理时,PostgreSQL计算冻结限制txid,并冻结t_xmin小于冻结限制txid的元组。
其中“OldestXmin”是当前运行的事务中最早的txid。例如,如果在执行VACUUM命令时有三个事务(txids 100、101和102)正在运行,“OldestXmin”是100。如果当前没有其它事务, “OldestXmin”是执行此真空命令的txid。这里,vacuum_freeze_min_age是一个配置参数(默认值50000000)
| 模式 | 触发时机 |
| lazy | 普通 VACUUM(自动或手动) 只要 表的 relfrozenxid 还没超过 vacuum_freeze_table_age |
| eager | 当 表的 relfrozenxid ≥ vacuum_freeze_table_age 或 手动 VACUUM FREEZE |
vacuum_freeze_table_age 默认 150 000 000(1.5 亿事务)
3.1 懒惰模式(Lazy Mode)
冻结过程会标记元组的t_infomask 标志位

懒惰模式会根据VM文件跳过 不包含死元组的页
3.2 急切模式(Eager Mode)
满足以下条件时,启动急切模式
pg_database.datfrozenxid<(OldestXmin−vacuum_freeze_table_age)
急切模式弥补了懒惰模式的缺陷。它根据VM文件进行判断,所有元组都已冻结的页面会被跳过,更新相关的系统目录,并在可能的情况下删除不必要的文件和clog页面。
Clog文件记录了事务的状态,可用于元组的可见性判断。已被冻结的元组都是可见的,因此就不需要保留相应的clog文件。
4 Autovacuum
自动清理过程,需要打开参数
autovacuum = on # ( ON by default )
track_counts = on # ( ON by default )
4.1 触发条件
Autovacuum操作的实际内容:
1)vacuum;
2)Analyze
Autovacuum vacuum触发条件(如果由于更新和删除,表中的实际死元组数超过此有效阈值,则该表将成为autovacuum的候选表):
Autovacuum VACUUM thresold for a table = autovacuum_vacuum_scale_factor * number of tuples + autovacuum_vacuum_threshold
Autovacuum ANALYZE触发条件(自上次分析以来插入/删除/更新总数超过此阈值的任何表都有资格进行autovacuum分析)
Autovacuum ANALYZE threshold for a table = autovacuum_analyze_scale_factor * number of tuples + autovacuum_analyze_threshold
4.2 相关参数
| 参数名 | 单位/默认 | 一句话作用 | 调优提示 |
| autovacuum | bool/on | 总开关;off=完全禁用后台自动清理 | 生产必须 on,只临时排障可关 |
| autovacuum_max_workers | int/3 ★ | 控制自动清理(autovacuum)进程的最大并发数 | CPU≥8 核可→6;IO 吃紧时先提 worker 再降 cost |
| autovacuum_naptime | s/60 ★ | 两次清理操作之间的等待时间 ,控制 autovacuum 的运行频率。较短的时间间隔意味着 autovacuum 更频繁地运行。 | 大库可把 60→30 让“大表”更快被扫描到 |
| autovacuum_vacuum_threshold | int/50 ★ | 触发 VACUUM 基础行数:deadtuple ≥ 50 + 0.2×reltuples | 大表→0,靠比例即可 |
| autovacuum_vacuum_scale_factor | float/0.2 ★ | 与上行组成“20% 变化就扫” | 10 TB 级表→0.05 甚至 0.01,减少膨胀 |
| autovacuum_analyze_threshold | int/50 | 触发 ANALYZE 基础行数 | 同 threshold,通常同步调 |
| autovacuum_analyze_scale_factor | float/0.1 | 10% 变化就 ANALYZE | 可略低于 vacuum_factor |
| autovacuum_vacuum_cost_limit | int/-1 | 本表 cost 上限;-1=取 vacuum_cost_limit | IO 慢盘→提高 200→1000 |
| autovacuum_vacuum_cost_delay | ms/-1 | autovacuum 每次清理操作之间的延迟时间。它用于控制 autovacuum 的资源消耗,避免对生产环境造成过大压力。 较低的值意味着 autovacuum 在清理时会更积极地运行,但可能会对系统性能产生更大影响。;-1=取 vacuum_cost_delay | 与 cost_limit 反比,可调 0 加速 |
| autovacuum_freeze_max_age | xid/200 000 000 ★ | 表 relfrozenxid 距当前 XID 超此值→强制 freeze | 提前→150 000 000 减少 eager VACUUM |
| autovacuum_multixact_freeze_max_age | xid/400 000 000 | MultiXact 版同上 | 通常同比例下调 |
| autovacuum_vacuum_insert_threshold | int/1000 | ≥17 新增:INSERT 行数基础值 | _append-only 表→0 靠因子 |
| autovacuum_vacuum_insert_scale_factor | float/0.2 | INSERT 20% 也 vacuum | append-only→0.05 防新页膨胀 |
| log_autovacuum_min_duration | ms/-1 ★ | ≥此毫秒→日志;-1=不记,0=全记 | 排障时→0,日常→250 ms |
| vacuum_cost_limit | int/200 | 控制 VACUUM 操作(清理和回收空间)的资源消耗限制。它定义了在每次 VACUUM 操作中,允许消耗的最大“成本”(以单位计)。成本是根据 I/O 和 CPU 消耗计算的。 较高的值允许 VACUUM 操作更积极地使用资源,而较低的值则限制其资源使用,以减少对生产环境的影响。 | 与 autovacuum_vacuum_cost_limit 联动 |
| vacuum_cost_delay | ms/0 | vacuum 每次清理操作之间的延迟时间 | IO 吃紧→2-10 ms,SSD→0 |
| vacuum_freeze_min_age | xid/50 000 000 | 页级 freeze 最小 XID 年龄 | 通常不动;调大→少写,调小→早 freeze |
| vacuum_freeze_table_age | xid/150 000 000 ★ | 表级 eager freeze 阈值 | 与 freeze_max_age 同步下调 |
| vacuum_multixact_freeze_table_age | xid/150 000 000 | MultiXact 版同上 | 同上 |
使用口诀
“worker+naptime 决定扫多快;factor+threshold 决定扫不扫;cost+delay 决定扫多猛;freeze_max_age 决定扫多狠;log_duration 看扫多慢。”
5 相关查询脚本
#查询pg_class.relfrozenxid和pg_database.datfrozenxid
SELECT n.nspname as "Schema", c.relname as "Name", c.relfrozenxid
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r','')
AND n.nspname <> 'information_schema' AND n.nspname !~ '^pg_toast'
AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY c.relfrozenxid::text::bigint DESC;
SELECT datname, datfrozenxid FROM pg_database WHERE datname = 'testdb';
#查询某张表的空闲空间比例
testdb=# CREATE EXTENSION pg_freespacemap;
CREATE EXTENSION
testdb=# SELECT count(*) as "number of pages",
pg_size_pretty(cast(avg(avail) as bigint)) as "Av. freespace size",
round(100 * avg(avail)/8192 ,2) as "Av. freespace ratio"
FROM pg_freespace('accounts');
#查询检查指定表的每页的可用空间比率
SELECT *, round(100 * avail/8192 ,2) as "freespace ratio"
FROM pg_freespace('accounts');
#查询表上的插入/删除/更新数
SELECT n_tup_ins as "inserts",n_tup_upd as "updates",n_tup_del as "deletes", n_live_tup as "live_tuples", n_dead_tup as "dead_tuples"
FROM pg_stat_user_tables
WHERE schemaname = 'scott' and relname = 'employee';
Comments NOTHING