
PostgreSQL 表膨胀治理不是单纯执行一次 VACUUM FULL 就结束的问题。它真正要解决的是:频繁更新或删除数据后,表和索引里留下大量死元组,查询扫描更多无效页面,磁盘占用持续上涨,备份和恢复窗口也被拖长。本文会教你如何判断膨胀是否已经影响业务,并把 Vacuum 与 Autovacuum 调优变成一套可复查、可回滚、可持续的运维流程。
如果你的业务运行在自管数据库、VPS(虚拟专用服务器)或独立服务器上,数据库维护不能只依赖默认参数。默认 Autovacuum 适合一般负载,但在订单、日志、队列、会话、统计表这类高更新场景下,默认触发阈值往往偏宽松。和常规服务器性能优化一样,PostgreSQL 表膨胀治理也需要先量化问题,再按表分级处理。
先理解 Vacuum 到底清理什么
PostgreSQL 使用 MVCC(多版本并发控制)保证读写并发。一次 UPDATE 通常不是原地覆盖旧行,而是写入新版本;一次 DELETE 也不会立刻把磁盘空间还给操作系统,而是把旧版本标记为后续可回收。Vacuum 的核心工作就是识别这些已经没有事务需要读取的旧版本,让空间可以被后续写入复用,并更新可见性信息,帮助查询少做无效扫描。
这也是很多误区的来源:普通 VACUUM 通常不会缩小数据文件,它主要让表内部空间可复用;VACUUM FULL 才会重写表并释放磁盘空间,但它需要更重的锁,维护窗口也更长。因此,生产环境里更常见的策略不是频繁做 VACUUM FULL,而是让 Autovacuum 更早、更稳定地工作,避免膨胀积累到必须停机处理。
你可以先用下面的查询观察死元组和统计信息,重点看高更新表是否长期积累:
SELECT
schemaname,
relname,
n_live_tup,
n_dead_tup,
last_vacuum,
last_autovacuum,
last_analyze,
last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;
这里的 n_dead_tup 是估算值,不适合当作精确账本,但足够用来定位优先级。如果一张表 n_dead_tup 长期高于 n_live_tup 的 10% 到 20%,同时慢查询和磁盘增长都集中在它身上,就应该进入下一步排查。
判断膨胀时不要只看磁盘大小
只看 pg_relation_size() 很容易误判。大表不一定膨胀,小表也可能因为更新频繁导致索引效率变差。更稳妥的判断方式,是把表大小、死元组比例、索引大小、业务访问模式放在一起看。

在日常巡检里,我们建议至少固定 3 个观察维度:第一,pg_stat_user_tables 里的死元组和最近 Autovacuum 时间;第二,pg_stat_user_indexes 里的索引扫描次数和索引大小;第三,业务侧慢查询是否集中在同一批表。这样做的好处是,你不会因为某张历史归档表很大就误判,也不会忽略一张只有几 GB 但每分钟大量更新的热点表。
下面这条 SQL 可以快速列出表和索引空间占用:
SELECT
schemaname,
relname,
pg_size_pretty(pg_total_relation_size(relid)) AS total_size,
pg_size_pretty(pg_relation_size(relid)) AS table_size,
pg_size_pretty(pg_indexes_size(relid)) AS index_size,
n_live_tup,
n_dead_tup
FROM pg_stat_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 20;
如果某张表总大小增长很快,但业务数据量没有同步增长,且 last_autovacuum 长时间不更新,通常说明自动清理跟不上写入节奏。对线上站点来说,这类问题还会间接影响页面响应时间;如果你同时在做TTFB 与主机响应优化,数据库膨胀应该和缓存、PHP 进程、磁盘 I/O 一起排查。
Autovacuum 参数应该按表调,而不是全局一刀切
Autovacuum 的触发大致由固定阈值和比例阈值共同决定。常见参数包括 autovacuum_vacuum_threshold、autovacuum_vacuum_scale_factor、autovacuum_analyze_threshold、autovacuum_analyze_scale_factor、autovacuum_naptime 以及成本限制相关参数。问题在于,全局参数对小表和大表的效果完全不同:一张 100 万行表和一张 10 亿行表,如果都用相同 scale factor,后者可能要积累大量死元组才会触发。
更实际的做法是先保守调整热点表的存储参数。例如,对高更新订单表或任务队列表,可以把触发比例调低,让 Autovacuum 更早介入:
ALTER TABLE public.orders SET (
autovacuum_vacuum_scale_factor = 0.02,
autovacuum_vacuum_threshold = 5000,
autovacuum_analyze_scale_factor = 0.01,
autovacuum_analyze_threshold = 5000
);
这组参数不是通用答案,而是一个起点。它表达的策略是:当死元组达到约 2% 且超过 5000 行时更早清理,统计信息也更频繁刷新。对于写入量更高的表,还要结合 CPU、磁盘 I/O 和业务低峰期观察,避免清理任务与业务写入互相争抢资源。
成本参数要和机器资源一起看
很多团队只降低 scale factor,却忽略 Autovacuum worker 的处理能力。如果清理任务触发更频繁,但 autovacuum_max_workers 太少,或成本限制太紧,任务仍然会排队。可以重点观察日志中的 autovacuum 执行时长、被取消次数,以及表级 last_autovacuum 是否真的更新。
对于中小型业务,先不要激进提高所有 worker。更稳的路径是挑 3 到 5 张热点表做表级调优,观察 24 到 72 小时,再决定是否调整全局 autovacuum_max_workers、autovacuum_vacuum_cost_limit 和 autovacuum_naptime。如果数据库和 Web 服务同机部署,还要考虑 CPU、内存和磁盘队列;这类部署在 VPS(虚拟专用服务器)场景里很常见,资源余量比单纯参数更关键。
手动 Vacuum 用来止损,不该替代自动治理
当膨胀已经影响查询或磁盘空间逼近上限时,手动维护仍然必要。普通 VACUUM (ANALYZE) 适合在业务低峰期补一次清理和统计信息刷新;VACUUM FULL 适合处理已经明显膨胀且必须归还磁盘空间的表,但要提前确认锁等待、维护窗口和回滚方案。

可以用下面的顺序降低风险:先对目标表做只读评估,确认大小、死元组、索引占用和业务访问窗口;再在低峰期执行普通 Vacuum;如果磁盘仍然无法释放,再评估 VACUUM FULL、重建索引或在线重组工具。执行前请确保备份可用,尤其是数据库运行在单台服务器时,磁盘空间不足会让维护动作本身变成风险源。
VACUUM (ANALYZE, VERBOSE) public.orders;
如果必须执行重写类操作,要准备至少 3 项检查:剩余磁盘空间是否足够容纳重写过程的临时增长;应用层是否能接受表级锁或短暂停写;备份恢复是否在最近一次演练中通过。对依赖 WordPress、WooCommerce 或自研后台的站点,也可以把数据库治理和WordPress 速度优化放在同一个维护计划里,避免只优化页面缓存却放任数据库持续膨胀。
把治理流程固化成周巡检
一次参数修改只能解决当前表的问题,长期稳定要靠巡检。我们建议把 PostgreSQL 表膨胀治理拆成 4 个固定动作,并把结果记录到同一份运维表或监控备注里:
- 每周导出前 20 张
n_dead_tup最高的表,并标注last_autovacuum时间。 - 对更新量最高的 3 到 5 张表单独配置 Autovacuum 参数,不直接改大全局范围。
- 每次维护前后记录
pg_total_relation_size(),至少保留 4 周趋势。 - 慢查询日志里同一张表连续 3 天出现时,把它加入重点观察清单。
这套流程的价值在于可追踪。你能看到某个参数调整后,死元组是否下降、慢查询是否减少、磁盘增长是否放缓,而不是等到空间报警时才临时处理。对于外贸站、会员系统、工单系统这类有明显业务高峰的站点,维护窗口也应和访问时段错开,避免在订单提交或批量同步期间触发重清理。
如果你的数据库部署在 Hostease 的 VPS(虚拟专用服务器)或服务器方案上,可以结合实例监控看 CPU、磁盘 I/O、内存和备份窗口,再决定 Autovacuum 参数是否需要更积极。资源层面的瓶颈同样会影响清理效率,单靠 SQL 参数无法弥补磁盘性能或容量规划不足。
总结:先控制膨胀速度,再处理历史包袱
PostgreSQL 表膨胀治理的推荐顺序是:先确认死元组和空间增长是否真实影响业务,再对热点表降低 Autovacuum 触发阈值,最后把手动 Vacuum 或重写操作放进可控维护窗口。不要一上来全局调大参数,也不要把 VACUUM FULL 当作日常任务;前者可能制造资源争抢,后者可能带来锁等待和维护风险。
更稳妥的建议是,从 3 张最热的表开始,用 24 到 72 小时观察参数效果;如果慢查询、磁盘增长和死元组趋势都明显改善,再把同样方法扩展到同类表。若你同时关注网站性能、数据库稳定性和主机资源规划,也可以参考VPS(虚拟专用服务器)相关方案,按照业务写入量、备份频率和维护窗口选择更合适的资源配置。核心目标不是把所有指标调到漂亮,而是让清理速度长期跟得上业务写入速度。