PostgreSQL 教程: 调优内部统计信息 pg_stats

七月 22, 2026

摘要:在本教程中,您将学习到 PostgreSQL 内部统计信息工作原理与优化指南。

目录

核心观点

查询计划器(Query Planner)在制定 SQL 执行计划时,并不会直接扫描原始数据表,而是高度依赖数据库的统计信息。这些核心统计信息存放在 pg_statistic 系统表中,而 pg_stats 是为其提供的高可读性视图。(注:在 PostgreSQL 的命名规范中,复数形式的通常是视图,单数形式的通常是表)。

pg_stats 关键统计指标解析

image

当你执行 ANALYZE 命令后,系统会扫描表数据并生成以下关键统计信息:

  • n_distinct (唯一值数量):衡量列中不重复值的数量。如果是主键(每行均唯一),该值在数据库中会特殊显示为 -1

  • null_frac (空值比例):代表该列中 Null 值所占的百分比。

  • correlation (相关性):表示数据在磁盘上物理分布的有序程度,数值介于 -1 到 1 之间。

    • 接近 0:代表数据物理分布非常随机,计划器会倾向于选择 顺序扫描 (Sequential Scan) 以避免高昂的随机读取代价。
    • 接近 -1 或 1:代表数据物理分布高度有序,计划器会更倾向于选择 索引扫描 (Index Scan)
  • most_common_values (MCV) 与 most_common_freqs:记录列中最常出现的值及其对应的百分比频率。这能帮助计划器准确预判数据倾斜情况。

代价计算与扫描方式对比(索引扫描 vs. 顺序扫描)

image

查询计划器会基于“代价 (Cost)”这一数学模型来决定使用哪种扫描方式,其中包含三个核心代价指标:

  1. random_page_cost:从磁盘中随机拉取一个数据页的代价(索引扫描常用)。
  2. seq_page_cost:顺序读取连续数据页的代价(顺序扫描常用)。
  3. cpu_tuple_cost:CPU 处理单行数据(Tuple)的代价。

💡 观点对比:为什么有时查询带索引,却依然触发全表顺序扫描? 如果查询条件匹配的数据量极大(如占总行数的 20%),通过索引在百万行数据中进行上万次“随机页面抓取”所消耗的 CPU 周期和时间,会远超直接从头到尾遍历整张表。因此,计划器会判定顺序扫描成本更低。相反,当查询条件命中极少行数(如偏冷门数据)时,计划器才会果断选择索引扫描。

统计信息的进阶调整策略

默认的统计信息可能无法反映复杂的数据分布,此时可通过以下两种方案进行进阶调整:

1. 直方图 (Histograms) 精度调整

  • 适用场景:时间段、数值区间等范围查询。
  • 问题所在:PostgreSQL 默认只收集 100 个数据桶 (Buckets),在查询精细的范围数据时容易出现统计偏差。
  • 解决方法:使用 ALTER TABLE ... ALTER COLUMN ... SET STATISTICS 1000 将某一列的统计精度提升至 1000 甚至更高。
  • 注意:精度调高会导致 ANALYZE 耗时变长,建议仅针对具体列调整,切勿进行全局修改。

image

2. 扩展统计信息 (Extended Statistics)

  • 适用场景:处理多列强相关的联合查询(例如:city = 'Cheyenne' AND state = 'Wyoming')。
  • 问题所在:计划器默认将多列条件视为独立事件并把概率相乘,从而严重低估返回行数。
  • 解决方法:使用 CREATE STATISTICS 命令来显式声明列之间的依赖关系,帮助计划器获得准确的关联预估。

image

慢查询排查 6 步法清单

当发现数据库查询缓慢且行为不符合预期时,可按照以下 6 步进行排查:

  1. 对比预估与实际行数:运行 EXPLAIN ANALYZE 命令,从执行计划最深层的节点(Deepest node)开始排查,找出“预估行数”与“实际行数”偏差严重的地方。
  2. 检查 pg_stats 现状:检查偏差列的 n_distinct、高频值数组和直方图,确认它们是否符合你对实际业务数据的了解。
  3. 手动触发 ANALYZE:特别是在大批量数据导入或迁移后,系统自动清理机制(auto-vacuum)可能尚未介入,此时必须手动执行 ANALYZE 以更新过期的统计信息。
  4. 提高统计精度:如果分析后预估依然不准,尝试将该列的统计目标值(Statistics Target)提高至 1000 甚至 10000。
  5. 建立关联统计:如果偏差节点涉及两列或以上的过滤条件,请评估其相关性,并为其创建扩展统计信息(Extended Statistics)。
  6. 重写查询逻辑:如果穷尽上述手段后查询依然低效,请考虑重写 SQL 语句或从其他业务维度寻找性能提升的方法。

image

结论

“查询计划器的聪明程度,完全取决于你喂给它的统计信息。”

开发者务必确保统计信息准确并维持在合理的范围内,定期执行分析(Analyze),这是优化 PostgreSQL 查询性能最根本且有效的基础。

参考

pg_stats: How Postgres Internal Stats Work

了解更多

PostgreSQL 优化