# 51.5.规划师/优化器

51.5.1. 制定可能的计划

委员会的任务规划师/优化器就是制定一个最佳的执行计划。一个给定的SQL查询(以及查询树)实际上可以以多种不同的方式执行,每种方式都会产生相同的结果集。如果计算上可行,查询优化器将检查每个可能的执行计划,最终选择预计运行最快的执行计划。

# 笔记

在某些情况下,检查每种可能的查询执行方式都会占用大量的时间和内存。尤其是在执行涉及大量联接操作的查询时,会发生这种情况。为了在合理的时间内确定合理的(不一定是最优的)查询计划,PostgreSQL使用遗传查询优化器(见第60章)当连接数超过阈值时(请参见盖库_门槛).

planner的搜索过程实际上与名为路径,它们只是计划的简化表示,只包含计划制定者做出决策所需的信息。在确定了最便宜的路径后,一个成熟的计划树是为了传给遗嘱执行人的。这充分详细地表示了所需的执行计划,以供执行者运行。在本节的其余部分中,我们将忽略路径和计划之间的区别。

# 51.5.1.制定可能的计划

planner/optimizer首先生成扫描查询中使用的每个关系(表)的计划。可能的计划由每个关系的可用指标决定。始终存在对关系执行顺序扫描的可能性,因此始终会创建顺序扫描计划。假设在关系上定义了索引(例如B树索引),并且查询包含该限制关系属性OPR常数如果关系属性恰好与B树索引的键匹配OPR是索引中列出的运算符之一操作员类,使用B树索引创建另一个计划来扫描关系。如果存在进一步的索引,并且查询中的限制恰好与索引的键匹配,则将考虑进一步的计划。索引扫描计划也会为索引生成,这些索引的排序顺序可以与查询的排序顺序相匹配订购人子句(如果有的话),或可能对合并联接有用的排序顺序(见下文)。

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

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

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

  • 散列连接:首先扫描正确的关系并将其加载到哈希表中,使用其连接属性作为哈希键。接下来,将扫描左侧关系,并将找到的每一行的适当值用作哈希键,以定位表中匹配的行。

    当查询涉及两个以上的关系时,最终结果必须由一个连接步骤树建立,每个步骤有两个输入。计划者检查不同的可能连接序列,以找到最便宜的连接序列。

    如果查询使用少于盖库_临界点关系,进行近乎穷举的搜索以找到最佳连接序列。计划器优先考虑在任意两个关系中存在对应的连接子句的连接在哪里资格(即,对于它的限制,如其中 rel1.attr1=rel2.attr2存在)。仅当没有其他选择时才考虑没有连接子句的连接对,即特定关系没有任何其他关系的可用连接子句。为计划者考虑的每个连接对生成所有可能的计划,并选择(估计)最便宜的一个。

    什么时候geqo_threshold超出时,所考虑的连接序列由启发式确定,如中所述第 60 章.否则过程相同。

    完成的计划树包括基本关系的顺序或索引扫描,以及需要的嵌套循环、合并或哈希连接节点,以及所需的任何辅助步骤,例如排序节点或聚合函数计算节点。这些计划节点类型中的大多数都有额外的能力来做选择(丢弃不满足指定布尔条件的行)和投影(基于给定列值计算派生列集,即在需要时评估标量表达式)。计划者的职责之一是附加选择条件在哪里子句和计算所需的输出表达式到计划树的最合适的节点。