Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

50.5 计划人员/优化程序#

50.5.1. 生成可能的计划

计划人员/优化程序的任务是创建最佳执行计划。给定的 SQL 查询(因此,一个查询树)实际上可以用各种不同的方法执行,每种方法都会生成相同的结果集。如果在计算上可行,查询优化程序将检查这些可能的执行计划中的每一个,最终选择预计运行最快的执行计划。

注意

在某些情况下,检查查询执行的每种可能方式会占用过多时间和内存。具体而言,这是在执行涉及大量联接操作的查询时发生的。为了在合理的时间内确定合理的(不一定是最佳的)查询计划,PostgreSQL 使用遗传查询优化程序(参见章节 60),当联接数量超过阈值(参见geqo_threshold)时。

计划程序的搜索过程实际上使用称为路径的数据结构,这些结构只是包含计划的精简表示,其中仅包含计划人员做出决策所需的信息。确定最短路径后,将构建一个完整的计划树以传递给执行程序。它以足够详细的方式表示期望的执行计划,以便执行程序运行它。在本节的其余部分中,我们将忽略路径和计划之间的区别。

50.5.1 生成可能的计划#

规划器/优化器从生成对于查询中使用的每个关系(表)进行扫描的计划开始。可能的计划由每个关系上可用的索引决定。总有对一个关系进行顺序扫描的可能性,所以总是创建一个顺序扫描计划。假设一个索引定义在一个关系上(例如一个 B 树索引),并且一个查询包含限制 relation.attribute OPR constant。如果 relation.attribute 碰巧匹配 B 树索引的键,并且 OPR 是索引的 操作符类中列出的操作符之一,可以使用 B 树索引创建一个计划来扫描关系。如果有更多的索引并且查询中的限制碰巧匹配一个索引的键,将会考虑更多的计划。如果索引有一个排序顺序,可以匹配查询的 ORDER BY 子句(如果有的话),或者一个可能对合并连接有用的排序顺序,对它们也生成索引扫描计划(见下文)。

如果查询需要连接两个或更多的关系,则在找到所有可行的单一关系扫描计划后考虑连接关系的计划。三种可用的连接策略是

  • 嵌套循环连接:对于左关系中找到的每一行,右关系被扫描一次。这个策略易于实现,但是可能非常耗时。(不过,如果右关系可以使用索引扫描扫描,这可能是一个好策略。可以将左关系当前行的值用作右关系索引扫描的键。)

  • 合并连接:在连接开始前,每个关系按连接属性排序。然后两个关系并行扫描并且匹配行合并形成连接行。这种连接很有吸引力,因为每个关系只需要被扫描一次。所需的排序可以由一个显示的排序步骤实现,或者通过使用连接键上的索引以正确顺序扫描关系来实现。

  • 哈希连接:右关系首先被扫描并且加载到一个哈希表中,使用其连接属性作为哈希键。接下来左关系被扫描并且找到的每一行的值被用作哈希键以在表中找到匹配行。

如果查询涉及两个以上的关系,最终结果必须通过一个连接步骤的树来构建,每个连接步骤有两个输入。规划器检查不同的可能的连接序列以找到最经济的一个。

如果查询使用低于 geqo_threshold 的关系,将执行近乎详尽的搜索以找出最好的连接顺序。规划程序优先考虑任意两个关系之间的连接,因为在 WHERE 限定符中有相应的连接子句(即,存在诸如 where rel1.attr1=rel2.attr2 的限制)而这些关系存在相应的连接子句。只有在别无选择时才考虑没有连接子句的连接对,即特定关系没有任何可与任何其他关系连接的连接子句。规划程序考虑的每对连接都会生成所有可能的计划,并且选择其中的最便宜的计划(估计为最便宜的计划)。

当超过 geqo_threshold 时,考虑的连接顺序由启发式方法决定,如 第 60 章 所述。否则,进程相同。

完成的计划树由基本关系的顺序或索引扫描组成,以及根据需要嵌套循环、合并或哈希连接节点,加上任何需要的辅助步骤,例如排序节点或聚合函数计算节点。其中大部分计划节点类型还具有执行 选择(舍弃不满足特定布尔条件的行)和 投影(基于给定列值计算派生列集,也就是说,根据需要评估标量表达式)附加功能。规划程序的职责之一是从 WHERE 子句附加选择条件,并计算计划树最合适的节点的所需输出表达式。