PostgreSQL的VACUUM默认不释放磁盘空间,仅标记死元组为可复用;VACUUM FULL才真正收缩文件并归还空间给操作系统,但需排他锁、阻塞读写且代价高昂。

PostgreSQL 的 VACUUM 不会立即把被 DELETE 释放的磁盘空间还给操作系统,除非你用对了模式和参数。
为什么 DELETE 后表文件没变小?
PostgreSQL 使用 MVCC,DELETE 只是把行标记为“可覆盖”,不真正擦除数据,也不收缩物理文件。这些“死亡元组”堆积在页内,形成内部碎片。只有 VACUUM 扫描并清理它们,才可能让后续插入复用空间——但默认情况下,它仍不会把空闲页归还 OS。
-
VACUUM(无参数):只回收内部空间,供本表后续INSERT复用;不释放磁盘空间 -
VACUUM FULL:重写整个表,释放空间给操作系统;但需排他锁,阻塞读写,且无法在主从复制中安全使用 - 自动
autovacuum默认只做普通VACUUM,不会触发FULL
VACUUM FULL 的真实代价和适用场景
它确实能缩小 pg_class.relpages 和实际文件大小,但代价很高:全程锁表、高 I/O、临时双倍磁盘占用(新旧表同时存在),且期间 WAL 暴涨。只应在以下情况考虑:
- 表长期只删不插,且确认后续写入极少(如归档日志表)
- 磁盘空间极度紧张,且业务允许数分钟到数小时的写入中断
- 表体积不大(
- 不在主库上执行;若必须,先切主从再操作
示例:
VACUUM FULL VERBOSE my_large_log_table;加
VERBOSE 能看到实际释放页数和耗时,别跳过这步。
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
更安全的替代方案:CLUSTER + 建索引
如果目标是释放空间又不想锁死表,CLUSTER 是比 VACUUM FULL 更可控的选择——但它依赖一个现有索引,且同样需要排他锁(不过锁时间短得多):
- 先建一个常用查询字段的索引(如
CREATE INDEX idx_on_ts ON logs (created_at)) - 执行
CLUSTER logs USING idx_on_ts:按索引顺序重排数据,自然压缩空页 - 重排后
ANALYZE logs更新统计信息
注意:CLUSTER 不会像 VACUUM FULL 那样清空所有死亡元组,所以建议先跑一次普通 VACUUM 再 CLUSTER。
日常维护该怎么做?
靠单次手动操作治标不治本。关键在预防和自动化:
- 确保
autovacuum开启(默认开启),并调低autovacuum_vacuum_scale_factor(比如设为0.02)对高频删除表 - 对频繁
DELETE的表,定期运行VACUUM (ANALYZE, VERBOSE) table_name,观察输出中的dead row version count和pages that are entirely empty - 监控
pg_stat_all_tables.n_dead_tup,持续 > 10% 总行数就该干预 - 避免在事务里批量
DELETE百万行——拆成千行一批,减少长事务和锁等待
真正难的是判断“何时必须释放磁盘”:多数时候,只要 autovacuum 跟得上,内部空间复用足够高效,不必强求文件变小。

















