PostgreSQL Executor(3): 可优化语句的执行

可优化语句经过优化器优化后生成查询计划树,并由Executor执行。Executor对外有四个接口函数:ExecutorStartExecutorRunExecutorFinishExecutorEnd。Executor的输入是查询描述符QueryDesc,输出是结果数据或相关的执行信息。

查询描述符封装了Executor执行查询需要的一切信息。QueryDesc定义在src/include/executor/execdesc.h中。

执行查询计划树,只需要构造QueryDesc,并依次调用上面四个接口函数就能完成执行过程。

Executor的接口函数全部在Portal的执行过程中被调用。以PORTAL_ONE_SELECT的执行策略为例:

  1. PortalStart中首先确定执行策略。如果执行策略是PORTAL_ONE_SELECT,则会创建QueryDesc,将查询计划树赋给查询描述符。然后执行ExecutorStart完成Executor的初始化工作。

  2. PortalRun中调用PortalRunSelect,在其中执行ExecutorRun,完成查询计划的执行。

  3. PortalDrop中调用PortalCleanup,在其中执行ExecutorFinishExecutorEnd以清理环境,最后释放QueryDesc

Executor的处理模式

Executor对查询计划树的执行过程,实际上是对计划树的每一个节点的处理。查询树的每一个节点都表示一种操作,节点的处理被设计成按需要驱动的模式。节点使用子节点输出的数据作为输入,按自身的操作逻辑处理之后向上层节点返回结果数据。实现上,从根节点开始处理,每个节点在处理过程中根据需要调用子节点的处理过程来获取数据。通过递归的方式,实现整个计划树的遍历执行。

初始化和清理操作也是采用相同的模式,从根节点开始递归处理子节点。

计划树中的每一个节点都是一个操作符,完成一个具体的物理操作。在PostgreSQL中,操作符被定义为有0~2个输入和1个输出。这样所有的操作符可以组织成一个二叉树,下层节点的输出是上层节点的输入,直至根节点对外输出结果数据。数据(元组)从叶子节点向上层节点流动,直至根节点完成处理。

在Executor中,通过ExecInitNodeExecProcNodeExecEndNode三个入口函数统一对节点进行初始化、执行和清理。每个节点都实现了对应的初始化、执行和清理函数,并且通过三个入口函数从根节点开始递归执行。

PostgreSQL采用一次一个元组的执行模式,每个节点一次向上层节点返回一个元组。整个查询计划树的节点就构成了一个管道,查询计划树的执行过程可以看成拉动元组穿过管道的过程。

计划节点的数据结构

PostgreSQL采用面向对象的思想设计节点的数据结构,所有节点都继承自PlanPlan是所有节点的通用抽象类型。

Plan的定义在src/include/nodes/plannodes.h中,定义如下:

/* ----------------
 *        Plan node
 *
 * All plan nodes "derive" from the Plan structure by having the
 * Plan structure as the first field.  This ensures that everything works
 * when nodes are cast to Plan's.  (node pointers are frequently cast to Plan*
 * when passed around generically in the executor)
 *
 * We never actually instantiate any Plan nodes; this is just the common
 * abstract superclass for all Plan-type nodes.
 * ----------------
 */
typedef struct Plan
{
    NodeTag     type;

    /*
     * estimated execution costs for plan (see costsize.c for more info)
     */
    Cost        startup_cost;   /* cost expended before fetching any tuples */
    Cost        total_cost;     /* total cost (assuming all tuples fetched) */

    /*
     * planner's estimate of result size of this plan step
     */
    double      plan_rows;      /* number of rows plan is expected to emit */
    int         plan_width;     /* average row width in bytes */

    /*
     * information needed for parallel query
     */
    bool        parallel_aware; /* engage parallel-aware logic? */
    bool        parallel_safe;  /* OK to use as part of parallel plan? */

    /*
     * Common structural data for all Plan types.
     */
    int         plan_node_id;   /* unique across entire final plan tree */
    List       *targetlist;     /* target list to be computed at this node */
    List       *qual;           /* implicitly-ANDed qual conditions */
    struct Plan *lefttree;      /* input plan tree(s) */
    struct Plan *righttree;
    List       *initPlan;       /* Init Plan nodes (un-correlated expr
                                 * subselects) */

    /*
     * Information for management of parameter-change-driven rescanning
     *
     * extParam includes the paramIDs of all external PARAM_EXEC params
     * affecting this plan node or its children.  setParam params from the
     * node's initPlans are not included, but their extParams are.
     *
     * allParam includes all the extParam paramIDs, plus the IDs of local
     * params that affect the node (i.e., the setParams of its initplans).
     * These are _all_ the PARAM_EXEC params that affect this node.
     */
    Bitmapset  *extParam;
    Bitmapset  *allParam;
} Plan;

Plan中定义了左右子树(lefttree, righttree)、节点类型(type)、选择表达式(qual)、投影列表(targetlist)等公共字段。

PostgreSQL将所有的计划节点按功能分为四类:

  • 控制节点(control node)
  • 扫描节点(scan node)
  • 连接节点(join node)
  • 物化节点(materalization node)

其中,扫描和连接节点类型定义了公共父类ScanJoin。具体的节点继承了公共父类并增加了与自身操作相关的扩展字段。

节点通过左右子树指针链接了子节点,根节点指针保存在PlannedStmt中。而PlannedStmt被存放在QueryDesc`中。

PostgreSQL为每一种计划节点定义了一个状态节点。与计划节点类似,所有的状态节点都继承自PlanState,其中包含计划节点指针、执行器全局状态结构指针、投影运算信息、选择运算条件,以及左右子状态节点指针。状态节点之间组成了与计划树类似的状态树。

在执行器初始化时,ExecutorStart会根据查询计划树构造执行器全局状态(EState)以及计划节点状态树。在查询树执行过程中,执行器将使用状态节点记录计划节点的执行状态和数据,并通过全局状态在节点间传递元组。执行器的清理函数ExecutorEnd将回收执行器全局状态和状态节点。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,183评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,850评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,766评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,854评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,871评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,457评论 1 311
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,999评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,914评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,465评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,543评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,675评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,354评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,029评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,514评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,616评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,091评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,685评论 2 360

推荐阅读更多精彩内容

  • --- layout: post title: "如果有人问你关系型数据库的原理,叫他看这篇文章(转)" date...
    蓝坠星阅读 796评论 0 3
  • 不可优化语句包括DDL、DCL等。与DML语句不同,它们的处理方式是为每一个类型的语句提供相应的处理函数。 Pos...
    DavidLi2010阅读 778评论 0 52
  • Zookeeper用于集群主备切换。 YARN让集群具备更好的扩展性。 Spark没有存储能力。 Spark的Ma...
    Yobhel阅读 7,284评论 0 34
  • 从看到华为辞退34岁以上老员工开始,内心受到很大的冲击。也许现在还在大公司打工的你是个白领,不愁吃穿,每月有丰厚的...
    艺州海棠阅读 412评论 0 0
  • 1. 今天我很丰盛。早上起来给家人做早餐,尝试新的做法。反响不错。 2. 今天我很丰盛。手上有好几件事情要同时进行...
    daodaobai阅读 111评论 0 0