postgresql的delete仅标记行删除而不释放磁盘空间,需vacuum回收死元组供内部重用,vacuum full才释放空间给操作系统但需锁表;autovacuum可能滞后,应监控死元组并调优参数;执行vacuum full前须确认磁盘空间、业务低峰及连接池配置。

PostgreSQL的DELETE不会立即释放磁盘空间
PostgreSQL中执行DELETE只是把行标记为“已删除”,并不真正从数据文件中移除,也不归还磁盘空间给操作系统。这些被标记的“死元组”(dead tuples)会持续占用空间,直到被VACUUM清理。如果表频繁更新/删除,pg_class.relpages可能远大于实际有效数据所需页数,查询性能和备份体积都会受影响。
VACUUM和VACUUM FULL的区别很关键
VACUUM是在线操作:它回收死元组占用的空间,供后续INSERT或UPDATE复用(即“内部重用”),但不缩减表文件大小,也不释放空间给操作系统。VACUUM FULL则不同——它会重写整个表、压缩数据、释放空闲页,并把收缩后的文件大小交还给操作系统。但它需要独占表锁(ACCESS EXCLUSIVE),阻塞所有读写,且期间生成大量WAL和临时磁盘IO。
- 日常维护用
VACUUM(或更推荐的VACUUM ANALYZE) - 只有确认磁盘空间严重紧张、且能接受停写窗口时,才考虑
VACUUM FULL -
VACUUM FULL不能在备库执行;主库执行时,备库会回放重写过程,可能引发同步延迟
自动清理(autovacuum)不是万能的
PostgreSQL默认开启autovacuum,但它按触发阈值(如vacuum_threshold + vacuum_scale_factor * reltuples)启动,对大表或高并发删除场景可能滞后。常见症状包括:pg_stat_all_tables.n_dead_tup持续高位、pg_stat_progress_vacuum长时间无活动、SELECT pg_size_pretty(pg_total_relation_size('tbl'))明显膨胀。
- 检查是否启用:
SHOW autovacuum;(应为on) - 调低敏感度可加急清理:修改表级参数
ALTER TABLE tbl SET (autovacuum_vacuum_scale_factor = 0.01); - 紧急手动清理:直接运行
VACUUM VERBOSE tbl;,加VERBOSE能看到清理了多少死元组 - 注意:
VACUUM不阻塞DML,但会持有SHARE UPDATE EXCLUSIVE锁,与CREATE INDEX CONCURRENTLY等少数操作冲突
执行VACUUM前必须确认的三件事
盲目执行VACUUM FULL可能让系统雪上加霜。先确认:
- 是否有足够临时磁盘空间?
VACUUM FULL需约等于表大小的额外空间用于重写 - 是否在业务低峰期?可通过
SELECT * FROM pg_locks WHERE locktype = 'relation' AND mode = 'AccessExclusiveLock';查当前有无长事务阻塞 - 是否已关闭连接池的“连接复用”逻辑?某些连接池(如PgBouncer in transaction pooling mode)会阻止
VACUUM FULL获取必要锁
对超大表,优先考虑分批DELETE + VACUUM,或用pg_repack替代VACUUM FULL——它不锁表,但需额外安装扩展。










