可優(yōu)化語句經(jīng)過優(yōu)化器優(yōu)化后生成查詢計劃樹洞慎,并由Executor執(zhí)行告抄。Executor對外有四個接口函數(shù):ExecutorStart
痒玩、ExecutorRun
、ExecutorFinish
搂妻、ExecutorEnd
蒙保。Executor的輸入是查詢描述符QueryDesc
,輸出是結(jié)果數(shù)據(jù)或相關(guān)的執(zhí)行信息欲主。
查詢描述符封裝了Executor執(zhí)行查詢需要的一切信息邓厕。QueryDesc
定義在src/include/executor/execdesc.h
中逝嚎。
執(zhí)行查詢計劃樹,只需要構(gòu)造QueryDesc详恼,并依次調(diào)用上面四個接口函數(shù)就能完成執(zhí)行過程补君。
Executor的接口函數(shù)全部在Portal的執(zhí)行過程中被調(diào)用。以PORTAL_ONE_SELECT
的執(zhí)行策略為例:
在
PortalStart
中首先確定執(zhí)行策略昧互。如果執(zhí)行策略是PORTAL_ONE_SELECT
赚哗,則會創(chuàng)建QueryDesc
,將查詢計劃樹賦給查詢描述符。然后執(zhí)行ExecutorStart
完成Executor的初始化工作硅堆。在
PortalRun
中調(diào)用PortalRunSelect
,在其中執(zhí)行ExecutorRun
贿讹,完成查詢計劃的執(zhí)行渐逃。在
PortalDrop
中調(diào)用PortalCleanup
,在其中執(zhí)行ExecutorFinish
和ExecutorEnd
以清理環(huán)境民褂,最后釋放QueryDesc
茄菊。
Executor的處理模式
Executor對查詢計劃樹的執(zhí)行過程,實際上是對計劃樹的每一個節(jié)點的處理赊堪。查詢樹的每一個節(jié)點都表示一種操作面殖,節(jié)點的處理被設(shè)計成按需要驅(qū)動的模式。節(jié)點使用子節(jié)點輸出的數(shù)據(jù)作為輸入哭廉,按自身的操作邏輯處理之后向上層節(jié)點返回結(jié)果數(shù)據(jù)脊僚。實現(xiàn)上,從根節(jié)點開始處理遵绰,每個節(jié)點在處理過程中根據(jù)需要調(diào)用子節(jié)點的處理過程來獲取數(shù)據(jù)辽幌。通過遞歸的方式,實現(xiàn)整個計劃樹的遍歷執(zhí)行椿访。
初始化和清理操作也是采用相同的模式乌企,從根節(jié)點開始遞歸處理子節(jié)點。
計劃樹中的每一個節(jié)點都是一個操作符成玫,完成一個具體的物理操作加酵。在PostgreSQL中,操作符被定義為有0~2個輸入和1個輸出哭当。這樣所有的操作符可以組織成一個二叉樹猪腕,下層節(jié)點的輸出是上層節(jié)點的輸入,直至根節(jié)點對外輸出結(jié)果數(shù)據(jù)荣病。數(shù)據(jù)(元組)從葉子節(jié)點向上層節(jié)點流動码撰,直至根節(jié)點完成處理。
在Executor中个盆,通過ExecInitNode
脖岛、ExecProcNode
和ExecEndNode
三個入口函數(shù)統(tǒng)一對節(jié)點進(jìn)行初始化朵栖、執(zhí)行和清理。每個節(jié)點都實現(xiàn)了對應(yīng)的初始化柴梆、執(zhí)行和清理函數(shù)陨溅,并且通過三個入口函數(shù)從根節(jié)點開始遞歸執(zhí)行。
PostgreSQL采用一次一個元組的執(zhí)行模式绍在,每個節(jié)點一次向上層節(jié)點返回一個元組门扇。整個查詢計劃樹的節(jié)點就構(gòu)成了一個管道,查詢計劃樹的執(zhí)行過程可以看成拉動元組穿過管道的過程偿渡。
計劃節(jié)點的數(shù)據(jù)結(jié)構(gòu)
PostgreSQL采用面向?qū)ο蟮乃枷朐O(shè)計節(jié)點的數(shù)據(jù)結(jié)構(gòu)臼寄,所有節(jié)點都繼承自Plan
。Plan
是所有節(jié)點的通用抽象類型溜宽。
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)、節(jié)點類型(type)适揉、選擇表達(dá)式(qual)留攒、投影列表(targetlist)等公共字段。
PostgreSQL將所有的計劃節(jié)點按功能分為四類:
- 控制節(jié)點(control node)
- 掃描節(jié)點(scan node)
- 連接節(jié)點(join node)
- 物化節(jié)點(materalization node)
其中嫉嘀,掃描和連接節(jié)點類型定義了公共父類Scan
和Join
炼邀。具體的節(jié)點繼承了公共父類并增加了與自身操作相關(guān)的擴(kuò)展字段。
節(jié)點通過左右子樹指針鏈接了子節(jié)點剪侮,根節(jié)點指針保存在PlannedStmt
中拭宁。而PlannedStmt被存放在
QueryDesc`中。
PostgreSQL為每一種計劃節(jié)點定義了一個狀態(tài)節(jié)點票彪。與計劃節(jié)點類似红淡,所有的狀態(tài)節(jié)點都繼承自PlanState
,其中包含計劃節(jié)點指針降铸、執(zhí)行器全局狀態(tài)結(jié)構(gòu)指針在旱、投影運算信息、選擇運算條件推掸,以及左右子狀態(tài)節(jié)點指針桶蝎。狀態(tài)節(jié)點之間組成了與計劃樹類似的狀態(tài)樹。
在執(zhí)行器初始化時谅畅,ExecutorStart
會根據(jù)查詢計劃樹構(gòu)造執(zhí)行器全局狀態(tài)(EState
)以及計劃節(jié)點狀態(tài)樹登渣。在查詢樹執(zhí)行過程中,執(zhí)行器將使用狀態(tài)節(jié)點記錄計劃節(jié)點的執(zhí)行狀態(tài)和數(shù)據(jù)毡泻,并通過全局狀態(tài)在節(jié)點間傳遞元組胜茧。執(zhí)行器的清理函數(shù)ExecutorEnd
將回收執(zhí)行器全局狀態(tài)和狀態(tài)節(jié)點。