SQL 统计信息(ANALYZE / VACUUM ANALYZE)的更新频率与 autovacuum 调优

3次阅读

是,autovacuum 会自动触发 analyze,但依赖变更行数占比和上次统计时间,由 autovacuum_analyze_threshold 和 autovacuum_analyze_scale_factor 共同决定,默认为 50 行 + 10% 表行数。

SQL 统计信息(ANALYZE / VACUUM ANALYZE)的更新频率与 autovacuum 调优

autovacuum 会自动触发 ANALYZE 吗?

会,但不是每次写入都触发,也不是固定时间间隔。它依赖表的变更行数占比和上次统计时间,由 autovacuum_analyze_thresholdautovacuum_analyze_scale_factor 共同决定。

默认值是:阈值 50 行 + 比例 10%(即 50 + 当前表总行数 × 0.1)。小表(比如只有 200 行)改 70 行就满足条件;大表(1000 万行)得改满 100 万行才可能触发。

  • 如果业务频繁更新小维度表(如 status_codes),默认配置下 ANALYZE 可能长期不跑,导致执行计划劣化
  • autovacuum_analyze_scale_factor = 0 可禁用比例项,只看绝对变更量,适合变动频繁的小表
  • 修改后需连接到对应数据库执行 ALTER table ... SET (autovacuum_analyze_scale_factor = 0),全局参数只影响新表

手动 ANALYZE 要不要加 VERBOSE?

日常维护不用,VERBOSE 只在排查统计偏差时临时启用,它会输出每列采样行数、实际 distinct 值数量等调试信息,但不改变行为,也不提升精度。

典型误用场景:把 ANALYZE VERBOSE 当“更彻底的统计”来定期跑——其实和普通 ANALYZE 用的是同一套采样逻辑,只是多打几行日志。

  • ANALYZE 默认对每列采样约 300 行(受 default_statistics_target 影响),并非全表扫描
  • 想提高某列统计质量,应单独设置: ALTER TABLE t ALTER column c SET STATISTICS 1000
  • VERBOSE 输出里出现 "sample block" = N 表示实际读了 N 个数据页,可用于判断是否因数据稀疏导致采样不足

VACUUM ANALYZE 和分开执行 ANALYZE 有啥区别?

前者先做 VACUUM(清理死元组、更新可见性映射),再做 ANALYZE;后者只更新统计信息。两者不等价,也不能互相替代。

常见错误是以为 VACUUM ANALYZE “更全面”,结果在高并发更新的表上频繁执行,反而引发 I/O 尖峰和锁等待。

  • VACUUM 阶段会持有 ShareUpdateExclusiveLock,阻塞 CREATE INDEXALTER TABLE 等操作
  • 如果只是因为执行计划变差而怀疑统计过期,优先跑 ANALYZE,不是 VACUUM ANALYZE
  • 对只读大表(如数仓事实表),可以禁用 autovacuum(autovacuum_enabled = off),但必须定期手动 ANALYZE,否则优化器永远看不到新分区的数据分布

统计信息不准时,EXPLAIN 显示的 rows 和实际相差十倍以上怎么办?

先确认是不是采样不足或数据倾斜导致,而不是立刻调大 default_statistics_target。盲目设成 1000 或 5000 容易让 ANALYZE 变慢、占用更多 shared_buffers,并不能解决根本问题。

postgresql 的统计直方图只存最常见值(MCV)和等宽直方图,遇到长尾分布(比如用户积分从 0 到 1 亿)、或大量 NULL 值,就容易误判。

  • select * FROM pg_stats WHERE tablename = 't' AND attname = 'c' 查看 n_distinctmost_common_vals 是否合理
  • 如果某列 NULL 占比超 30%,考虑建部分索引:CREATE INDEX idx ON t(c) WHERE c IS NOT NULL,并确保 WHERE 条件里也写明 c IS NOT NULL
  • 对时间戳字段,按月分区后,每个子表单独 ANALYZE 比在父表上跑一次更有效

统计信息不是越新越好,也不是越多越好;关键是在变更敏感点及时触发,且采样能覆盖真实分布形态。很多人卡在“不知道该信哪一行数字”,其实应该先信 pg_stats 里的 histogram_bounds,再对照业务逻辑看它是否真的漏掉了关键分界点。

text=ZqhQzanResources