跳转到主要内容

53 VACUUM 与 ANALYZE

使用 VACUUM、ANALYZE 和自动清理维护行可见性与规划器统计信息。

PostgreSQL 使用多版本并发控制(MVCC):UPDATE 不会覆盖一行,而是写入一个新版本,并将旧版本标记为死行。DELETE 只会将行标记为死行——它们仍留在磁盘上。VACUUM 回收这部分空间,并防止事务 ID 回卷。ANALYZE 更新规划器的统计信息,使其能够做出合理的决策。PostgreSQL 通过自动清理自动运行这两个命令,但理解它们做什么,以及何时需要手动干预,是成为合格运维人员的一部分。

本章要点

  • MVCC 为什么会产生死行,以及 VACUUM 如何处理它们
  • VACUUM、VACUUM FULL 与 VACUUM ANALYZE 的区别
  • ANALYZE 实际计算了什么
  • 读取 pg_stat_user_tables,查看自动清理活动
  • 事务 ID 回卷的临界点,以及为什么不能忽视它

示例

示例 53.1 VACUUM 与 ANALYZE
-- Inspect the per-table autovacuum stats
SELECT relname, n_live_tup, n_dead_tup,
       last_vacuum, last_autovacuum,
       last_analyze, last_autoanalyze
FROM pg_stat_user_tables
WHERE schemaname = 'public'
ORDER BY n_dead_tup DESC
LIMIT 5;

-- Manually vacuum and analyze a table
VACUUM (VERBOSE) fruits;
ANALYZE VERBOSE fruits;

-- Combined: VACUUM ANALYZE (most common manual invocation)
VACUUM ANALYZE fruits;

-- See the planner's stored statistics for a column
SELECT attname, n_distinct, most_common_vals, most_common_freqs
FROM pg_stats
WHERE schemaname = 'public' AND tablename = 'fruits';

-- Bloat estimate (approximation): pg_stat_user_tables gives live vs dead row counts
-- pgstattuple extension gives exact numbers if you need them.

-- VACUUM FULL: rewrites the whole table, fully reclaims space, takes an exclusive lock.
-- Use sparingly.
-- VACUUM FULL fruits;

MVCC 让 PostgreSQL 能够实现高并发:由于每个事务都能看到一致的快照,写入者不会阻塞读取者,反之亦然。代价就是死行。VACUUM 遍历表,将死行占用的空间标记为可复用,并更新可见性映射(一种按页维护的位图,让仅索引扫描在标记为全可见的页面上无需检查堆可见性)。

普通 VACUUM 通常会让空间在表内变得可复用。它还可以截断表末尾的空页,并将这部分空间归还给操作系统。VACUUM FULL 会在新文件中重写整张表,释放旧文件——它能回收空间,但需要获取 ACCESS EXCLUSIVE 锁,而且速度很慢。很少使用它;日常维护由自动清理负责。

ANALYZE 对表进行抽样,以更新 pg_statistic。规划器使用这些统计信息——非重复值的数量、高频值和直方图——来估算行数并选择计划。过时的统计信息会产生糟糕的计划。

PostgreSQL 的自动清理守护进程会根据每张表的阈值(autovacuum_vacuum_scale_factor、autovacuum_analyze_scale_factor)自动运行 VACUUM 和 ANALYZE。自动清理负责日常维护,但仍需监控它的进度和阈值,尤其是在批量变更之后,或针对大型表和频繁更新的表。

运行示例

运行方式:

$ psql -f source/vacuum-and-analyze.sql postgres
 relname | n_live_tup | n_dead_tup | last_vacuum | ...
---------+------------+------------+-------------+----
 ...
INFO:  vacuuming "public.fruits"
INFO:  ...
VACUUM
 ...

常见陷阱

  • 为了“节省资源”而禁用自动清理——这几乎总是一个错误。死行会不断堆积,查询会变慢,最终追赶的代价远高于自动清理在稳定运行时的开销。
  • 在繁忙的表上运行 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 · 全部示例文件 · 准备工作