必须建扩展统计信息当多列JOIN或WHERE条件存在强相关性且EXPLAIN ANALYZE显示预估行数与实际行数偏差超10倍、Hash Join退化为Nested Loop、有复合索引却仍Seq Scan,或pg_stats中无对应组合直方图时;应使用CREATE STATISTICS声明dependencies类型并手动ANALYZE。

PostgreSQL 默认的单列统计信息不足以准确估算多列 JOIN 条件下的选择性,尤其当列间存在强相关性(比如 country 和 region 高度耦合)时,优化器会严重低估或高估行数,导致选错连接算法、弃用索引,甚至退化成嵌套循环全表扫描。解决它不靠调参,而要显式告诉优化器“这两列一起分布是什么样”。
什么时候必须建扩展统计信息?
出现以下任一现象,且 JOIN 条件含多个列(如 ON a.x = b.x AND a.y = b.y 或 WHERE a.x = ? AND a.y = ?):
-
EXPLAIN ANALYZE显示实际返回行数(rows=)和预估行数(rows=)相差 10 倍以上 - 执行计划里本该走
Hash Join却用了Nested Loop,且内表被反复全表扫描 - 明明有复合索引,但执行计划仍显示
Seq Scan而非Index Scan - 对同一张表加了
WHERE x = ? AND y = ?,但ANALYZE后统计直方图里most_common_vals对(x,y)的组合完全没体现
如何创建多列扩展统计信息?
用 CREATE STATISTICS 命令声明列组合的统计类型,再配合 ANALYZE 采集数据。不是所有组合都值得建——只针对真正存在统计偏差的列对:
- 语法:
CREATE STATISTICS stats_name ON col1, col2 FROM table_name - 必须指定统计类型,最常用的是
ndistinct(估算不同值组合数)和dependencies(列间函数依赖强度),例如:CREATE STATISTICS s_deps ON country, region FROM locations WITH (dependencies) - 建完不生效:必须手动执行
ANALYZE locations(不能只靠 autovacuum,它默认不处理扩展统计) - 验证是否生效:查系统表
pg_stats_ext,确认stxname和stxkeys正确;再看pg_stats_ext_exprs里是否有对应统计值
为什么 dependencies 比 ndistinct 更常用?
因为多数 JOIN 性能问题源于列间相关性被忽略,而非单纯组合基数不准:
-
dependencies让优化器知道 “当country = 'CN'时,region几乎总是'East'”,从而大幅修正选择性估算 -
ndistinct只告诉优化器 “(country, region)一共多少种组合”,对倾斜分布(比如 90% 行是('CN','East'))无感 - 实测中,加
WITH (dependencies)后,JOIN 行数预估误差常从 100x 降到 2x 内;而仅用ndistinct改善有限 - 注意:
dependencies需要足够样本量,若列组合唯一值太少(
容易踩的坑:建了也不生效?
扩展统计信息不是“建完就灵”,常见失效点:
- 忘记
ANALYZE:扩展统计本身不存数据,只是个元数据声明,必须触发ANALYZE才采集真实分布 - 列顺序写反:
ON a,b和ON b,a是两个不同统计对象,JOIN 条件顺序必须严格匹配声明顺序 - WHERE 中用了表达式:如
WHERE lower(country) = ?,扩展统计对原始列有效,对函数结果无效 - JOIN 类型影响:
LEFT JOIN右表的行数估算仍受左表驱动逻辑干扰,扩展统计主要改善等值条件本身的选择性,不解决语义约束带来的计划僵化
扩展统计信息本质是给优化器“补课”,但它无法替代合理的 JOIN 顺序和索引设计。一旦发现 EXPLAIN 里 Nested Loop 外层行数极小、内层却全表扫,先检查是否漏了 dependencies 统计——这往往是比调 work_mem 或重写 SQL 更直接的突破口。










