查询规划器根据预计工作量选择执行路径,并不会先把所有候选方案运行一遍、计时后再保留冠军。PostgreSQL 用统计信息和成本模型比较扫描、连接、排序等操作,因此选定的计划始终是建立在当前假设上的预测。诊断预测为何偏离实际,需要找出失效的具体假设,而不是把顺序扫描或嵌套循环当成天生有问题的节点。计划好不好,要放在完整查询的输入和输出要求中判断。
成本描述的是一个模型
PostgreSQL 的成本综合页面访问、元组处理、运算符计算等估计工作量,单位并不是毫秒。只需要拿到少量记录时,启动成本可能特别重要;必须产生完整结果时,总成本的意义更大。一个扫描如果已经提供了后面需要的顺序,就可能省掉排序,因此单独看来最便宜的扫描,不一定是整条查询最合适的输入。只比较一个节点会漏掉它给后续处理带来的影响。
规划搜索也有边界。数据库会考虑不同路径和连接顺序,而不是实际执行全部候选。连接关系很多时,PostgreSQL 可以启用遗传查询优化器来控制规划开销。可用索引、合法变换、配置与搜索选择共同限制了候选集合,所以不能把最后的选择描述为“对所有想象得到的实现都保证数学最优”。这种边界并不表示规划器不可信,只是说明它需要接受真实测量的检验。
坏掉的样子
统计刚刷新也可能估错。普通统计分别描述每一列,在组合两个过滤条件时可能假设它们相互独立,而实际两列总是一起变化。中间结果被低估后,反复查找看起来就比实际便宜,后续连接顺序也可能受到影响。如果模型本身没有表达这两列的关系,重复运行相同的分析不会凭空补上这部分知识。统计陈旧与统计种类不足,不能混成一种诊断。
扩展统计也只解决特定问题。函数依赖可以帮助估计与常量比较且彼此兼容的等值条件,但它不保存具体值之间的完整对应关系,也不是范围条件或任意跨列比较的一般证明。即使已经收集到很强的依赖,一组实际上互不兼容的常量仍可能被严重高估。因此验证中既需要兼容的正向情况,也需要一个不成立的组合来展示限制。
快速验证
在 PostgreSQL 18 的 psql 中执行下面的临时表实验。两列始终保存相同的值,一共有一百种不同值,每种重复一百次。所有修改都放在事务中,最后回滚会一并移除表和相应统计对象。
\set ON_ERROR_STOP on
BEGIN;
CREATE TEMP TABLE dimensions (a integer,b integer);
INSERT INTO dimensions SELECT g%100,g%100 FROM generate_series(1,10000) AS g;
ANALYZE dimensions;
EXPLAIN (ANALYZE, BUFFERS, TIMING OFF) SELECT * FROM dimensions WHERE a=7 AND b=7;
CREATE STATISTICS pg_temp.dimensions_dependency (dependencies) ON a,b FROM dimensions;
ANALYZE dimensions;
EXPLAIN (ANALYZE, BUFFERS, TIMING OFF) SELECT * FROM dimensions WHERE a=7 AND b=7;
EXPLAIN (ANALYZE, BUFFERS, TIMING OFF) SELECT * FROM dimensions WHERE a=7 AND b=8;
DO $$ BEGIN
IF (SELECT count(*) FROM dimensions WHERE a=7 AND b=7) <> 100 OR
(SELECT count(*) FROM dimensions WHERE a=7 AND b=8) <> 0 THEN RAISE EXCEPTION 'wrong correlated population'; END IF;
END $$;
ROLLBACK;
在建立扩展统计之前,PostgreSQL 18.6 对 a=7 AND b=7 估计一行,实际返回一百行。两个条件单独都选中百分之一的数据;把选择率相乘,就得到一万行中的万分之一,也就是一行。这套算术在条件独立时有意义,而夹具故意让两列完全相关,所以假设与数据并不相符。
创建函数依赖对象,告诉下一次 ANALYZE 要收集哪组列之间的关系。第二份计划估计一百行,实际也返回一百行。仅仅创建统计对象还没有填入所需信息,后续分析这一步不能省略。扫描仍然是顺序扫描,因为本例没有任何索引,仍要检查这张小表的全部内容。这里证明的是行数预测改善,不能由此声称应用已经变快,更不能把节点没有变化当成实验失败。
最后一个条件改成 a=7 AND b=8。查询实际返回零行,但函数依赖给出的估计仍是一百行。这个负向案例说明:知道一列决定另一列,并不等于这种统计知道“七只能对应七”这一条具体映射。末尾断言分别核对兼容与不兼容条件的正确结果。估计失真影响规划选择,却不会把本来不符合条件的记录变成合法查询结果。
根据偏差选择下一次实验
如果缺少的是常见值组合的信息,多列常见值列表能够表达这些组合。其他负载可能需要不同类型的统计、恰当的索引,或者让可索引条件更清晰的表达式改写。应当根据真实过滤条件增加对象,而不是为所有列组合都预先配置扩展统计;收集和使用不必要的统计也会消耗分析与规划资源。
比较替代方案时,固定数据集、参数、相关设置和结果要求,同时观察处理行数、缓冲区访问、内存溢写与实际延迟。局部节点便宜,却把更多工作推给上层,不能算整个查询改善。反过来,即使估计行数准确,也没有排除等待、昂贵读写或成本模型不适合当前环境的可能。应沿预计工作与实际工作之间的具体差异继续验证,再决定是否调整全局规划参数。
来源
- PostgreSQL 18:规划器与优化器,候选访问路径与连接策略,已核验 2026-09-10。
- PostgreSQL 18:规划器统计,列分布与扩展统计的限制,已核验 2026-09-10。
- PostgreSQL 18:多变量统计示例,相关条件与函数依赖估算,已核验 2026-09-10。