同一个查询有多种走法
一条SQL说明要取得怎样的结果,却通常没有逐项指定所有读取步骤。数据库可以顺序扫描表,也可以利用索引定位候选;多表连接又可以采用不同次序和连接算法。
PostgreSQL把这些选择组织为路径,比较后再构造执行计划树。树中除了扫描和连接,还可能有排序、聚合及过滤等步骤;优化器需要把条件和计算安放到能够正确执行的位置。
参考:[1]
预计成本不等于秒表读数
优化器利用行数、数据分布和访问成本等信息估计方案收益。若返回记录很多,顺序读取可能优于反复按索引访问;若只取极少记录,索引路径又可能明显合适。
EXPLAIN中的cost是由成本参数定义的估计单位,不是直接承诺多少毫秒。rows也通常表示节点预计输出量,而非所有检查过的行数;理解这些区别,才能判断估计偏差究竟发生在哪一步。
列之间的关联会进一步影响估计。若某份数据中A的值已经决定B,把A与B的筛选概率当作独立事件相乘,便可能低估同时满足条件的行数。PostgreSQL可为指定列组建立扩展统计,再由ANALYZE收集依赖信息;它不会替所有列组合自动承担这项成本。依赖统计也有限制,例如主要用于列与常量的简单等值及常量IN条件,不能假定它同样修复任意范围查询或所有连接估计。
规划自身也有成本
连接表数量增加时,枚举全部次序本身就可能耗费过多时间和内存。PostgreSQL在超过相关阈值后使用遗传查询优化等启发式办法,在有限规划成本内寻找合理方案。
因此优化器选择的是模型下预计较好的可行计划,而非给出数学上总能证明的全局最快结果。数据变化后应更新统计,并结合实际执行观察;不能因为计划用了索引,就跳过对访问量与总耗时的核对。