离总结我对查询分析模块的认识,已经过去了半个月多一点,在这段时间里,我也利用了解查询分析模块的方法对查询执行模块有了更多的了解。
对查询执行模块印象最深的,还是它对于执行策略的选择,以及将计划树计划转变为状态树的过程,这两步都是很有意义的步骤。前一步确定了将计划树转交给 Executor 模块还是 ProcessUtility 模块,后一步在 Plan 模块中起着重要作用,这一步既是在这个模块中实现的,也推动了物理执行计划的执行。
查询执行模块内部的结构主体是这样:
如你所见,这些个模块的主调函数还是 exec_simple_query() ,而查询分析模块的主调函数同样也是这个。
Portal模块
查询执行模块的第一个子模块便是 Portal 模块,它起到了决策的作用。当查询执行的前一个模块即查询编译模块输出执行计划时,Portal 模块会决定将这个执行计划传递给 ProcessUtility 模块还是 Executor 模块,因此,可以认为 Portal 模块就是一个选择器。当要执行的语句属于可优化语句,查询编译模块会为其生成一个或多个计划树,Portal 模块将会选择 Executor 模块来执行这些计划树操作;属于数据定义语句时,就交给 ProcessUtility 模块。针对这些流程,我写了以下几篇博客:
在这几篇博客中,我分析了 Portal 模块的主要函数即 PortalStart()、PortalRun() 以及 PortalDrop() ,最值得留意的是 PortalRun() ,由于函数之间的调用关系虽有层次但也复杂,所以我在博客中梳理了一下:
总而言之,我个人还是建议新手去看一下我写的这篇博客。那么,妥妥地,我接着总结。
Executor模块和Plan模块
在此之后,我便开始解析 Executor 模块,这个模块多执行增删改查语句,说到底是对元组的操作,比如说 INSERT 语句要将一个或多个元组存入特定表,DELETE 语句要从特定表中删除一个或多个元组,UPDATE 语句要更改符合 WHERE 子句条件的元组的一个或多个数据项,而 SELECT 语句则是要挑选符合 WHERE 子句条件的元组出来,这么说,大家可能就理解了。针对 Executor 模块,我写了以下几篇博客谈了自己的理解:
这两篇博客涉及到了该模块下主要的函数 ExecutorStart()、ExecutorRun() 以及 ExecutorEnd(),还有其它一些发挥了重要作用的记录执行状态的结构体比如 EState 结构体。然后继 Executor 模块之后,就自然而然地过渡到了 Plan 模块,这个模块用来对执行计划作进一步的细化。对此,我写了一篇博客分析:
另外,我还需要说明一点,Executor 模块和 Plan 模块并不是相互独立的模块,事实上,Executor 模块中的 standard_ExecutorStart()、standard_ExecutorRun()、standard_ExecutorEnd() 分别调用了 Plan 模块的 InitPlan()、ExecutePlan()、ExecEndPlan(),简而言之,就是包含关系,前者包含了后者。上图中的各个模块的前后关系其实是包含关系,这样画只是为了方便叙述。
Node模块
现在总结一下我对 Node 模块的理解,就像前面所说的,Node 模块也是融合在 Plan 模块中的。先用 ExecInitNode() 来将前一个模块传送过来的计划树转化为计划状态树,是从根节点开始按先遍历顺序一个个转化的,此后得到的计划状态树中是有一个指向存储了目标元组的特定内存区域的指针的,即 TupleDesc 结构体指针类型的 scandesc 变量。然后便是 ExecProcNode() 以中序遍历的顺序去执行状态节点,并根据节点的类型去调用不同的执行函数。还需要留意到的是,ExecProcNode() 每次只能从计划状态树获取一个元组,然而目标元组往往不止一条,该怎么办?大家还是到我的这篇博客以及前面关于 Plan 模块的博客里去找答案吧:
还是在这里说了吧,首先,正常情况下 Plan 模块的 ExecutePlan() 会在自身的大循环中不断地调用 ExecProcNode(),直到得到的并处理后的元组的个数达到了上限,也就是处理完了所有的数组,这时候就可以结束循环了。
ProcessUtility模块
这个模块就是用来处理数据定义语句的了,最终要调用的是 standard_ProcessUtility() 函数,我用一篇博客简要地分析了一下:
这个模块处理的还是语法分析树,而不是计划树,换句话说,该模块处理的语句本身是不可优化的了,比如建表语句 CREATE TABLE 等。
总结
总地来说,查询执行模块内部各个子模块间是有机联系在一起的,对于整体而言,它前接查询编译模块,得到计划树或是语法分析树,后接存储模块,可能和它会有动态的数据交换。另外,计划树和计划状态树由四类节点组成:Control node、Scan node、Join node、Materialization node,我虽然对此没有进行分析,但是就整体而言,它们代表着更加细化的操作。