pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
规划器/优化器的任务是创建一个最优的执行计划。它首先把查询中出现的各个关系所有可能的扫描和连接方式组合起来。所创建的所有路径都会得到同一结果,而优化器的任务就是估算执行每条路径的代价,并找出哪一条代价最低。
规划器/优化器根据查询中各关系上所定义索引的类型来决定应该生成哪些计划。对一个关系总是可以执行顺序扫描,因此总会创建一个只使用顺序扫描的计划。假设某个关系上定义了一个索引(例如一个 B-树索引),并且查询中包含限制条件 relation.attribute OPR constant。如果 relation.attribute 恰好匹配该 B-树索引的键,而且 OPR 不是 '<>',就会再创建一个使用该 B-树索引扫描该关系的计划。如果还存在其他索引,并且查询中的限制条件恰好匹配某个索引的键,则还会考虑更多计划。
在找出扫描单个关系的所有可行计划之后,就会创建连接各关系的计划。规划器/优化器只考虑在 where 限定条件中存在相应连接子句(即存在类似 where rel1.attr1=rel2.attr2 这样的限制)的每两个关系之间的连接。对于规划器/优化器所考虑的每一对连接,都会生成所有可能的计划。三种可用的连接策略是:
嵌套迭代连接:左关系中找到的每一个元组,都会使右关系被扫描一次。这种策略实现起来很容易,但可能非常耗时。
归并排序连接:在连接开始之前,每个关系都会先按照连接属性排序。然后,考虑到两个关系都已经按连接属性排好序,把它们归并到一起。这种连接方式更有吸引力,因为每个关系只需扫描一次。
哈希连接:首先在右关系的连接属性上建立哈希。接着扫描左关系,并将找到的每个元组中的相应值作为哈希键,用来在右关系中定位元组。
这里我们对计划中出现的节点做一点说明。图 \ref{plan} 展示了为 例 \ref{simple_select} 中的查询生成的计划。
计划的顶层节点是一个 MergeJoin 节点,它有两个后继,一个挂接在 lefttree 字段上,另一个挂接在 righttree 字段上。每个子节点代表连接中的一个关系。如前所述,归并排序连接要求每个关系都已排序,因此我们在每个子计划中都会看到一个 Sort 节点。查询中给出的附加限定条件(s.sno > 2)被尽可能下推,挂接到相应子计划的叶子 SeqScan 节点的 qpqual 字段上。
MergeJoin 节点 mergeclauses 字段上挂接的列表包含有关连接属性的信息。mergeclauses 列表(以及 targetlist)中出现的 VAR 节点,其 varno 字段的值 65000 和 65001 表示:应当考虑的不是当前节点的元组,而是下一个“更深”节点(即各子计划的顶层节点)的元组。
注意,图 \ref{plan} 中出现的每一个 Sort 和 SeqScan 节点都有一个 targetlist,但由于空间不足,图中只画出了 MergeJoin 节点的那一个。
规划器/优化器的另一项任务是在 Expr 和 Oper 节点中固定操作符 id。如前所述,Postgres 支持多种不同的数据类型,甚至可以使用用户自定义的类型。为了能够维护数量庞大的函数和操作符,必须把它们存储在一张系统表中。每个函数和操作符都会得到一个唯一的操作符 id。根据限定条件等之中所使用属性的类型,必须使用相应的操作符 id。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。