PostgreSQLNotes

第 11 章:统计信息

zjc 于 2026-01-11 发布

这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 优化器依赖统计信息估算行数。统计缺失、过期或表达式复杂,都可能导致错误计划。

11.1 ANALYZE

ANALYZE orders;
ANALYZE orders (user_id, status);

查看:

SELECT
    relname,
    n_live_tup,
    n_mod_since_analyze,
    last_analyze,
    last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_mod_since_analyze DESC;

autovacuum 会自动触发 analyze,但大表、突变负载和复杂查询仍可能需要手动窗口。

11.2 pg_stats

SELECT
    tablename,
    attname,
    n_distinct,
    null_frac,
    correlation
FROM pg_stats
WHERE schemaname = 'public'
  AND tablename = 'orders';

关键列:

含义
n_distinct 唯一值估算
null_frac NULL 比例
correlation 物理顺序相关性
most_common_vals 高频值
most_common_freqs 高频值频率
histogram_bounds 分布直方图

11.3 扩展统计

列相关时,单独统计会低估组合基数:

SELECT *
FROM orders
WHERE city = 'Shanghai' AND platform = 'App';

创建:

CREATE STATISTICS stat_orders_city_platform (dependencies)
ON city, platform FROM orders;

ANALYZE orders;

查看:

SELECT stxname, stxdependencies
FROM pg_statistic_ext;

11.4 统计目标

ALTER TABLE orders ALTER COLUMN status SET STATISTICS 500;
ANALYZE orders;

全局默认:

SHOW default_statistics_target;

目标越高采样越多、统计更细,但 analyze 成本和计划时间增加。热点复杂列可单独提高。

11.5 表达式统计

PostgreSQL 可对表达式收集统计。高频表达式查询可验证:

SELECT *
FROM pg_stats
WHERE tablename LIKE '%lower(email)%';

需要表达式索引或相应统计支持,否则复杂函数过滤可能严重低估或高估。

11.6 统计失效场景

场景 后果
大批量导入后未 analyze 估算过期
JSONB 内部字段 默认统计不足
跨列相关 低估组合过滤
自定义函数 难以估算
分区裁剪条件复杂 计划不稳定
数据倾斜 平均分布失真

11.7 分区表统计

分区表既要看父表,也要看子分区。新分区必须在数据加载后收集统计,否则首查计划可能异常。

ANALYZE events_2026_08;

11.8 估算问题排查

EXPLAIN ANALYZE
SELECT *
FROM orders
WHERE status = 'PAID'
  AND created_at >= now() - interval '1 day';

对比:

plan rows
actual rows
Rows Removed by Filter
loops

处理顺序:

  1. ANALYZE;
  2. 查 pg_stats;
  3. 检查表达式;
  4. 增加统计目标;
  5. 增加扩展统计;
  6. 改写 SQL。

本章小结

统计信息是优化器决策依据。日常要监控 n_mod_since_analyze,大表导入后及时分析,复杂列使用扩展统计和更高统计目标。估算修复往往比强行加索引更有效。

思考题

  1. ANALYZE 和 VACUUM 的职责有什么不同?
  2. correlation 会影响哪种扫描?
  3. 为什么跨列相关需要扩展统计?
  4. default_statistics_target 是否越大越好?
  5. 分区表统计要注意什么?