53 VACUUM 与 ANALYZE
PostgreSQL 使用多版本并发控制(MVCC):UPDATE 不会覆盖一行,而是写入一个新版本,并将旧版本标记为死行。DELETE 只会将行标记为死行——它们仍留在磁盘上。VACUUM 回收这部分空间,并防止事务 ID 回卷。ANALYZE 更新规划器的统计信息,使其能够做出合理的决策。PostgreSQL 通过自动清理自动运行这两个命令,但理解它们做什么,以及何时需要手动干预,是成为合格运维人员的一部分。
本章要点
- MVCC 为什么会产生死行,以及 VACUUM 如何处理它们
VACUUM、VACUUM FULL与VACUUM ANALYZE的区别ANALYZE实际计算了什么- 读取
pg_stat_user_tables,查看自动清理活动 - 事务 ID 回卷的临界点,以及为什么不能忽视它
示例
MVCC 让 PostgreSQL 能够实现高并发:由于每个事务都能看到一致的快照,写入者不会阻塞读取者,反之亦然。代价就是死行。VACUUM 遍历表,将死行占用的空间标记为可复用,并更新可见性映射(一种按页维护的位图,让仅索引扫描在标记为全可见的页面上无需检查堆可见性)。
普通 VACUUM 通常会让空间在表内变得可复用。它还可以截断表末尾的空页,并将这部分空间归还给操作系统。VACUUM FULL 会在新文件中重写整张表,释放旧文件——它能回收空间,但需要获取 ACCESS EXCLUSIVE 锁,而且速度很慢。很少使用它;日常维护由自动清理负责。
ANALYZE 对表进行抽样,以更新 pg_statistic。规划器使用这些统计信息——非重复值的数量、高频值和直方图——来估算行数并选择计划。过时的统计信息会产生糟糕的计划。
PostgreSQL 的自动清理守护进程会根据每张表的阈值(autovacuum_vacuum_scale_factor、autovacuum_analyze_scale_factor)自动运行 VACUUM 和 ANALYZE。自动清理负责日常维护,但仍需监控它的进度和阈值,尤其是在批量变更之后,或针对大型表和频繁更新的表。
运行示例
运行方式:
常见陷阱
- 为了“节省资源”而禁用自动清理——这几乎总是一个错误。死行会不断堆积,查询会变慢,最终追赶的代价远高于自动清理在稳定运行时的开销。
- 在繁忙的表上运行
VACUUM FULL——在整个运行期间,它会阻塞所有读写。使用pg_repack(扩展)进行在线压实。 - 批量导入后忘记运行
ANALYZE——在自动清理追赶上来之前,规划器没有新行的统计信息,可能会选择非常糟糕的计划。大量导入后,手动运行ANALYZE schema.table;。 - 长事务会阻止自动清理清除对其可见的行。在 psql 中遗留的
BEGIN;可能导致整个数据库的清理都无法进行——要留意pg_stat_activity中state = 'idle in transaction'的状态。
pg_stat_user_tables.n_dead_tup 是判断“是否需要清理”的权威指标。在繁忙表中,n_dead_tup / n_live_tup 比例较高,说明自动清理跟不上——使用 ALTER TABLE t SET (autovacuum_vacuum_scale_factor = 0.02); 调整每张表的阈值。
在一张表上运行一批 UPDATE,然后查看 pg_stat_user_tables 中的 n_dead_tup。运行 VACUUM 并再次检查。然后在 ANALYZE 前后分别对同一个查询运行 EXPLAIN ANALYZE,看看估算值是否发生变化。
下载
SQL 文件:vacuum-and-analyze.sql · 全部示例文件 · 准备工作