數(shù)據(jù)庫內(nèi)核分析之GPDB and PostgreSQL Portal
GPDB and PostgreSQL Portal內(nèi)核分析
0.導(dǎo)論
Portal(門戶),也稱為策略選擇模塊,根據(jù)sql語句類型選擇不同的執(zhí)行模塊(ProcessUtility、Executor)。
SQL語句類型包括:可優(yōu)化語句、數(shù)據(jù)定義語句。
可優(yōu)化語句包括DML,像insert/update/select等語句,這類語句特點是查詢滿足條件的元組返回給用戶或者元組操作后寫入磁盤,之所以稱之為可優(yōu)化語句是因為這類語句通常會被優(yōu)化器進行重寫與優(yōu)化,從而加快查詢速度。
數(shù)據(jù)定義語句主要是功能性語句,例如:DDL(Create、Alter、Drop)、DCL(Grant、COMMIT、ROLLBACK)等。
1.Portal
1.1 入口層
QD執(zhí)行會從exec_simple_query進入,QE執(zhí)行從exec_mpp_query進入。
1.2 Portal層
1.2.1 初識Portal
首先初識Portal內(nèi)部數(shù)據(jù)結(jié)構(gòu):
策略只包含一個SELECT查詢。
包含一個INSERT/UPDATE/DELETE查詢,且?guī)ETURNING條件。
包含一個SELECT查詢并且有修改的CTE。
例如:下面這個就不是PORTAL_ONE_MOD_WITH,而是PORTAL_MULTI_QUERY。
包含一個utility語句,且該語句執(zhí)行會返回像SELECT那樣有輸出結(jié)果。
其他情況,例如:PORTAL_MULTI_QUERY + PORTAL_MULTI_QUERY
- PORTAL_MULTI_QUERY
- PORTAL_UTIL_SELECT
- PORTAL_ONE_MOD_WITH
- PORTAL_ONE_RETURNING
- PORTAL_ONE_SELECT
狀態(tài)
- PORTAL_NEW
- PORTAL_DEFINED
- PORTAL_READY
- PORTAL_QUEUE
- PORTAL_ACTIVE
- PORTAL_DONE
- PORTAL_FAILED
這幾個狀態(tài)會在下面依次引入。
1.2.2 CreatePortal
創(chuàng)建一個新的Portal,傳入?yún)?shù)均為: CreatePortal("", true, true),表示創(chuàng)建一個匿名的Portal,允許重復(fù)且重復(fù)后保持沉默。
CreatePortal邏輯:
- 根據(jù)傳入的第一個參數(shù)name從哈希表中查找
- 根據(jù)傳入的第二個參數(shù)allowDup,如果第一步查找到,從哈希表中決定是否刪除。如果true,則刪除,否則報錯。在哈希表中查找到Portal且允許重復(fù)的情況下,在QD節(jié)點上會根據(jù)第三個參數(shù)dupSilent決定是否輸出告警信息。
- 創(chuàng)建一個新的Portal,并初始化相應(yīng)參數(shù)。
執(zhí)行完畢后,便創(chuàng)建好了一個狀態(tài)為PORTAL_NEW的Portal。
1.2.3 PortalDefineQuery
定義portal數(shù)據(jù),包含了:查詢語句sourceText、PlannedStmts、查詢完成標記qc。
注意:QD上根據(jù)傳遞進來的stmt來設(shè)置nodeTag,但是QE上為T_Query,因為QE上不是parsed statement,所以不是 T_SelectStmt。
最終設(shè)置Portal狀態(tài)為PORTAL_DEFINED。
1.2.4 PortalStart
準備好portal,主要有如下幾步:
- 設(shè)置ddesc,該信息為QD到QE上的額外信息,QD上為NULL,QE上不為NULL。
- 設(shè)置全局參數(shù),例如:當前活躍的portal、resourceOwner、context。
- 設(shè)置portal參數(shù)字段:portalParams,同樣QD上為NULL,QE上不為NULL。
- 設(shè)置portal策略(ChoosePortalStrategy)。
如下圖所示:輸入為查詢計劃鏈表,針對PORTAL_ONE_SELECT、PORTAL_ONE_MOD_WITH、PORTAL_UTIL_SELECT、PORTAL_ONE_RETURNING都是要求一個計劃,首先判斷是Query還是PlannedStmt,一般情況下的查詢語句基本都是PlannedStmt,對于像PREPARE st(int) as select * from t1之類utility語句第一次調(diào)用ChoosePortalStrategy返回PORTAL_MULTI_QUERY(命中PlannedStmt),第二次調(diào)用返回PORTAL_ONE_SELECT(命中Query)。
choose
5. 根據(jù)portal策略初始化portal,最重要的是初始化tupDesc與cursor postion。
例如:"QUERY PLAN"、"t1_id、col1"就是tupDesc。
不同tupleDesc函數(shù)區(qū)別
- ExecTypeFromTL
skip resjunk column
- ExecCleanTypeFromTL
with resjunk column
6. 在執(zhí)行Portal過程中發(fā)生異常,設(shè)置portal的狀態(tài)為PORTAL_FAILED;否則,下一步。
7. 設(shè)置Portal狀態(tài)為PORTAL_READY。
1.2.5 PortalRun
根據(jù)sql的語句類型選擇不同的執(zhí)行路徑,獲取元組數(shù)據(jù),完成portal工作,運行完之后要么Done要么下一輪(READY,而非ACTIVE)。
portal策略執(zhí)行路徑如下:
- PORTAL_ONE_SELECT
set result = portal->atEnd
- PORTAL_ONE_RETURNING|PORTAL_ONE_MOD_WITH|PORTAL_UTIL_SELECT
獲取時數(shù)據(jù)方向包含前進/后退
可以從holdStore中獲取,也可以從ExectorRun中獲取
填充holdStore(見下方)
調(diào)用PortalRunSelect返回n行數(shù)據(jù)
設(shè)置狀態(tài)為PORTAL_READY
設(shè)置是否完成運行標記為portal->atEnd
- PORTAL_MULTI_QUERY
調(diào)用PortalRunMulti
設(shè)置狀態(tài)為PORTAL_Done
設(shè)置是否完成運行標記為true
此外,上述圖中填充holdStore邏輯如下:
- 調(diào)用PortalCreateHoldStore填充portal->holdStore,通過工廠函數(shù)CreateDestReceiver構(gòu)造DestReceiver(子類:TStoreState);
- 根據(jù)portal策略執(zhí)行查詢:PORTAL_ONE_RETURNING、PORTAL_ONE_MOD_WITH 調(diào)用PortalRunMulti,PORTAL_UTIL_SELECT調(diào)用PortalRunUtility。
PortalRunUtility
PortalRunMulti
ProcessQuery
PortalRunUtility
utilityStmt
not utilityStmt
PORTAL_ONE_RETURNING、PORTAL_ONE_MOD_WITH
PORTAL_UTIL_SELECT
2.游標Cursor
2.1 打開游標
如果不想一次執(zhí)行整個命令,可以設(shè)置一個封裝該命令的游標(cursor), 然后每次讀取幾行命令結(jié)果。
例如:
該命令運行機制為:首先識別到是一個數(shù)據(jù)定義語句,便會調(diào)用ProcessUtility,隨后解析從PlannedStmt中的utilityStmt識別出是一個T_DeclareCursorStmt節(jié)點,調(diào)用PerformCursorOpen執(zhí)行Declare cursor命令。
PerformCursorOpen處理邏輯如下:
- query重寫
- 優(yōu)化器優(yōu)化,生成PlannedStmt
- 創(chuàng)建Portal(名字為游標名),僅調(diào)用PortalStart
2.2 關(guān)閉游標
關(guān)閉游標,實際就是關(guān)閉Portal,調(diào)用PerformPortalClose。
如下兩個操作:
如果傳入的名字為空,則是CLOSE ALL關(guān)閉所有非活躍portal,否則,只關(guān)閉指定的portal(cursor)。
2.3 FETCH or MOVE
FETCH與MOVE語法分別如下:
FETCH從游標中檢索n行到目標中, 目標可以是一個行變量、記錄變量、逗號分隔的普通變量列表, 就像SELECT INTO一樣, 如果沒有獲取到數(shù)據(jù),目標會設(shè)為NULL。
MOVE重新定位一個游標,而不需要檢索任何數(shù)據(jù),例如:一旦游標位置確定,則可以刪除或更新行。
從實現(xiàn)層面兩者都會進入到PerformPortalFetch,都被解析為FetchStmt,內(nèi)部有個成員ismove決定是MOVE還是FETCH。
不管是哪個,都會指定cursor名,有了這個名字,便知道了portal,隨后調(diào)用PortalRunFetch來獲取結(jié)果。
PortalRunFetch內(nèi)部會像PortalRun運行一樣,首先設(shè)置portal狀態(tài)為Active,隨后根據(jù)策略選擇不同的調(diào)用鏈。
- PORTAL_ONE_SELECT
調(diào)用DoPortalRunFetch
- PORTAL_ONE_RETURNING|PORTAL_ONE_MOD_WITH|PORTAL_UTIL_SELECT
首先判斷portal內(nèi)部是否有holdStore,如果沒有會調(diào)用FillPortalStore,隨后調(diào)用DoPortalRunFetch。
DoPortalRunFetch內(nèi)部實現(xiàn),會考慮傳入的direction,決定是前向還是后向等不同方向的掃描,最后調(diào)用PortalRunSelect獲取數(shù)據(jù),注意:gpdb不支持backward scan,但是pg支持。