PostgreSQL源码修改 ——查询优化(二)

第二章    问题分析——查询优化执行流程

2.1    问题总体分析

如前所述,我们的问题重点是在Executor部分,并且,我们将专注这个总要重要函数的详细流程及其重要的数据结构,因此,本章的行为思路是先分析整体流程,再分述几个修改将涉及的重要模块和函数流程,最后对执行过程中的重要数据结构做一个分析。

在分析的过程中,我们采取的分析方法是结合SourceInsigh + windowsKDevelope+虚拟机VMware Workstation。即在SourceInsigh阅读和定位源码,并在KDevelope中调试单步执行并观察其结果。在调试中,我们的示例执行语句是:select * from teacher order by name limits 2 offset 1

从第一章的问题描述小节,我们已经得知我们要做的事情。就是修改某个排序模块的某些执行步骤,使其达到优化的效果。这里所谓的某个排序模块的某些执行步骤就是我们这章分析和解决的问题,也是分析的目标。

2.2    查询优化整体流程

从前面的分析中,我已经知道用户输入的一个简单查询语句,经过前台的处理,系统从exec_simple_query 函数开始执行查询处理。所以我们着手从这里开始深入分析,下图为exec_simple_query函数的执行流程。此图来源于师姐的经验分析报告。因为觉得表达的确实很清晰,便不做改动,直接引用至此。

PotralStart

PortalRun

PortalRunSelect

调用ExecutorRun开始执行查询处理

调用ExecutePlan执行查询计划

调用ExecProcNode处理节点

循环调用自身,根据节点类型来调用相应处理

如果是T_LimitState调用ExecLimit

如果是T_SortState调用ExecSort排序

di

PortalDrop

case POTAL_ONE_SELECT

exec_simple_query

图表 1exec_simple_query总体流程

如图所示, exec_simple_query函数主要调用了PortalStart, PortalRun, PortalDrop 三个函数。PortalStart 主要做一些初始化准备工作, PortalDrop是在进行查询完毕之后的释放相关资源相关的等结尾工作。和我们项目相关的重点在PortalRun

PortalRun中,函数根据portal->strategy选择不同的语句执行策略,属于哪种情况,对于所给示例代码,属于PORTAL_ONE_SELECT分支,这样就调用PortalRunSelect函数开始处理,最后返回处理过的元组。

PortalRunSelect函数中,进一步调用了ExecutorRun,它是查询处理的关键函数,查询执行器的核心功能都是在这个函数中处理完成。

ExecutorRun它主要是通过调用ExecutePlan,将按照查询规划处理之后的返回值赋给结果变量result。而ExecutePlan函数则是按照生成的查询规划树开始执行相应处理。

ExecutePlan内部,递归遍历执行计划树,即调用ExecProcNode函数处理规划树上的每个节点,每次返回一个元组放入slot中去,如果返回元组为空则退出循环。每得到一个元组,根据其查询操作的类型对该元组进行相应处理,在我们的语句,属于CMD_SELECT情况,会继续调用到ExecSelect函数将以个slot赋给result

其中ExecProcNode函数是我们要重点分析和修改的部分。它处理一个规划树上的节点,并返回一个tuple。至此开始,它要开始循环调用自身,不断根据不同节点类型进行不同的处理。条件判断在一进入该函数时进行,在所给的示例语句中,计划树的结构如下图所示:

图表 2:示例语句的计划树结构

从上图可以看出,首先节点类型为T_LimitState,这样就会调用ExecLimit函数来处理。该函数我们将继续作为重点在下一张流程图中作分析。

ExecLimit中,将再次调用ExecProcNode函数,这时,它的左子节点是一个T_SortState类型,所以将会调用ExecSort函数进行排序,这个函数也是我们重点修改的,我们后面会单拿出来进行详细分析。

ExecSort也会调用其子结点T_SeqScanExecSeqScan来执行,这个函数不是我们关注的重点,故可以略过。

2.3       ExecLimit详细分析

2.3.1 ExecLimit关键代码及注释

TupleTableSlot * ExecLimit(LimitState *node)   /* return: a tuple or NULL */

{

     TupleTableSlot *slot;

     PlanState  *outerPlan;

     //获取子计划结点

     outerPlan = outerPlanState(node);

 

     /*

     *  ExecLimit状态变化及运动的逻辑主体

*/

     switch (node->lstate)

     {

     case LIMIT_INITIAL:   //处理offset限制

         //计算limit_countoffset等数据

         recompute_limits(node); 

         //判断参数是否合法

         if (node->count <= 0 && !node->noCount)

         {

              node->lstate = LIMIT_EMPTY;

              return NULL;

         }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值