Hollis Varden:删除行对 PostgreSQL 表有什么影响
内容提要
PostgreSQL 删除行后表在磁盘上不会缩小。实测 200 万行表删除一半后仍占 137MB,VACUUM 后不变,VACUUM FULL 才降至 68MB。VACUUM 仅使空间可复用,不归还操作系统。TRUNCATE 比 DELETE 快且 WAL 少,但会锁表且不触发触发器。将 fillfactor 设为 90 可提高 HOT 更新比例,使表更小。
延伸解读
VACUUM 的空间回收边界
文章实测显示,删除一半行后表大小不变,VACUUM 后仍为 137MB,只有 VACUUM FULL 才降至 68MB。这是因为 VACUUM 仅将死行空间标记为可复用,供后续插入使用,并不归还操作系统;唯一例外是文件末尾的空页会被截断。若删除模式导致每页仍有存活行,则文件大小不会减少。
TRUNCATE 与 DELETE 的权衡
清空表时,TRUNCATE 比 DELETE 快得多(0.05 秒 vs 3.5 秒),且 WAL 写入量极小(44 kB vs 125 MB),这对有副本或 WAL 归档的环境很重要。但 TRUNCATE 并非直接替代:它持有 ACCESS EXCLUSIVE 锁,不触发 ON DELETE 触发器,且在有外键引用时需级联或先处理引用表。
fillfactor 对更新性能的影响
将 fillfactor 设为 90 可为每页预留 10% 空间,提高 HOT 更新比例,避免索引更新。实测中,对 20 万行表更新非索引列三次,fillfactor 90 时 HOT 更新达 127,353 次,表最终比默认 fillfactor 100 小 24%。但作者指出,连续全表更新会快速消耗预留空间,实际分散更新的 HOT 比例可能更高。
Q&A
PostgreSQL 删除行后,表在磁盘上会缩小吗?
不会。删除行只是将行标记为死行,表文件大小不变。例如,一个 200 万行的表删除一半后,表大小仍为 137 MB。
VACUUM 和 VACUUM FULL 在回收空间上有什么不同?
VACUUM 仅将死行空间标记为可复用,不会将空间归还给操作系统,表文件大小不变;VACUUM FULL 会重写表并释放空间,但会加锁阻塞读写。例如,137 MB 的表 VACUUM 后仍为 137 MB,VACUUM FULL 后降至 68 MB。
TRUNCATE 和 DELETE 清空表时,性能和 WAL 生成量有何差异?
TRUNCATE 比 DELETE 快得多且生成的 WAL 少得多。例如,清空 100 万行表,DELETE 耗时 3.5 秒并生成 125 MB WAL,而 TRUNCATE 仅需 0.05 秒并生成 44 kB WAL。
使用 TRUNCATE 清空表有哪些限制或注意事项?
TRUNCATE 会获取 ACCESS EXCLUSIVE 锁,阻塞其他操作;不会触发 ON DELETE 触发器;如果其他表通过外键引用该表,则无法直接 TRUNCATE,除非同时 TRUNCATE 那些表或使用 CASCADE。
fillfactor 设置如何影响 PostgreSQL 的更新性能和表大小?
将 fillfactor 设为 90(即每页留 10% 空闲空间)可以提高 HOT 更新比例,减少索引更新,使表更小。例如,更新 20 万行三次后,fillfactor 100 时表为 61 MB,fillfactor 90 时表为 47 MB,缩小了 24%。
什么是 HOT 更新?它有什么好处?
HOT 更新是指当更新后的行版本能放在同一页且没有索引列被修改时,PostgreSQL 可以跳过索引更新的优化机制。这减少了索引维护开销,有助于保持表更小。