乾貨好文:分散式資料庫DDL的編譯與執行

roc_guo發表於2022-06-17
導讀 DDL 是資料定義語言,用於定義和管理 SQL 資料庫中的所有物件的語言,常用的 有:create,drop,alter 等。

DDL 是資料定義語言,用於定義和管理 SQL 資料庫中的所有物件的語言,常用的 有:create,drop,alter 等。通常來說,浪潮云溪資料庫 DDL 語句的流程主要分為四個部分,分別是邏輯計劃生成,物理計劃生成,計劃執行和 schemachange。本文主要介紹邏輯計劃生成,物理計劃生成和計劃執行。

邏輯計劃生成

邏輯計劃生成主要是生成一個 planNode 邏輯計劃節點,每一條 SQL 都會有自己的 planNode,以 create table 為例,該 SQL 生成的 planNode 中包含有 CREATETABLE statement 資訊例如表名,有該表所屬資料庫的資訊,還有該表的列的資訊等。planNode 生成後將其記錄到 planner 的 curplan(curplan 有當前計劃的屬性,包含抽象語法樹,planNode,子查詢計劃等)裡,主要流程如下圖所示。

乾貨好文:分散式資料庫DDL的編譯與執行乾貨好文:分散式資料庫DDL的編譯與執行

以 create table 語句為例,構建邏輯計劃主要是進行 memo 的構建以及 RBO 和 CBO 優化(memo 是用來儲存查詢計劃森林的一種資料結構)。首先會初始化一個優化計劃的上下文 optPlanningCtx,裡面會初始化優化器並記錄是否使用 memo cache 進行 memo 的快取複用,而這也是 DDL 與 DML 語句之間的區別,DDL 語句不會複用 memo。

構建 memo 會呼叫 builder 的 build () 方法,對於不同的 DDL 語句,會呼叫不同的方法去構建邏輯計劃。Create table 語句會呼叫 buildCreateTable, 構建 outScope,主要是生成語句的表示式 expr 記錄在 outScope 裡,最終記錄到 memo 裡面。而對於其他的 DDL 語句則會呼叫 tryBuildOpaque,然後通過 ConstructOpaqueDDL 函式構建 memo 的 expr。

Memo 構建完以後就是進行優化,通過 Optmize () 函式進入進行 RBO 和 CBO 優化,而 DDL 語句不會進行優化,這也是與 DML 語句的區別,DML 會將優化完的 memo 加入快取。然後就是進行邏輯計劃 planNode 的構建,DDL 語句中,create 語句會執行 buildCreateTable 進行 ConstructCreateTable 構建 createTableNode,而 drop 和 alter 語句則會執行 buildOpaque,通過 ConstructOpaque 返回所對應的 planNode,最後將構建好的 planNode 封裝到 planTop 裡面,並且如果是 DDL 語句會在 planTop 的 flags 裡面置 planFlagIsDDL。

物理計劃生成

邏輯計劃生成後,就會根據邏輯計劃生成物理計劃,主要的流程如下圖所示。

乾貨好文:分散式資料庫DDL的編譯與執行乾貨好文:分散式資料庫DDL的編譯與執行

在生成物理計劃之前會先判斷語句是否需要分散式執行,DDL 不會進行分散式執行,會生成一個本地的 PlanningCtx(PlanningCtx 包含在單個查詢的整個規劃過程中使用和更新的資料)。如果 SQL 語句有子句的話,則會呼叫 PlanAndRunSubqueries 函式先執行子句。DDL 語句會進入到 wrapPlan 進行物理計劃的生成。在 wrapPlan 中會深度優先遍歷 planNode 樹,找到第一個支援 DistSQL processor 的 planNode 然後在該 planNode 上遞迴 DistSQL 優化,如果有等效的 DistSQL 處理器呼叫 createPlanForNode 生成物理計劃。

函式首先判斷當前 planNode 的型別,呼叫對應的函式為 planNode 建立物理計劃 (如 indexJoinNode 會呼叫 createPlanForIndexJoin),DDL 則會遞迴呼叫 wrapPlan 將 planNode 包起來繼續處理 (wrapPlan 只包裝節點本身而不包括孩子節點)。然後,呼叫 shouldPlanTestMetadata 函式判斷是否需要進行後設資料處理,如果需要則新增相關資訊)。然後再進行建立 planNodeToRowSource(該結構體),如果被包裹的 planNode 是 flow 中的第一個 node,且語句型別是 RowsAffected(返回受影響行的計數的語句)則可以使用 fast path,planNode 子樹若支援 DistSQL 優化會在被優化後連線到 wrapper。返回 wrapper 後,為物理計劃新增 LocalProcessor 和 LocalProcessorIndexes 的元素,LocalProcessors 陣列包含了所有的 planNodeToRowSource,即被包裹的 planNode,記錄物理計劃的 ResultTypes。

每當新增新的 PhysicalPlan 時,都需要覆寫 ResultRouters,我們將只需要一個 result router,由於 local processor 不是分散式的,確保 p.ResultRouters 只有一個元素,最後填充計劃的 endpoints 就進入執行流程。

執行

執行過程的主要流程如下圖所示。

乾貨好文:分散式資料庫DDL的編譯與執行乾貨好文:分散式資料庫DDL的編譯與執行

StartExec 是具體執行 planNode 的方法,根據構建的物理計劃對錶描述符進行操作。StartExec 會先對 commend 遍歷(以 alter table 為例,commend 是一個表修改操作的切片),在遍歷中獲取叢集版本資訊,檢查在當前版本中是否支援新增列的型別,若支援則為 true,反之報錯;接著按照計劃中的列定義資訊生成列描述符,若新增的列是主鍵或含有唯一性約束,還會生成索引描述符,將這些描述符會包裝成一個個 mutation 並新增到表描述符的 Mutations 欄位裡;然後根據表、mutation 等資訊建立一個 job,job 也是通過系統表 system.jobs 進行維護,這個 job 是用來觸發及跟蹤 schema change 執行,接著將含有 mutations 的表描述通過 batch 更新到系統表 "descriptor" 裡。

執行流程中最主要的結構體就是 batch,表描述符會存在 batch 裡面,通過 writeTableDescToBatch 函式,在該函式裡面,首先判斷這個表如果不是一個新的表,需要將表的 version+1,然後會驗證表描述符格式是否良好,呼叫 addUncommittedTable 函式,這允許事務在繞過表租賃機制的情況下檢視自己的修改,最後是將表描述符的 KV 寫入到 batch 裡面。batch 構建完以後就執行 batchRequest。

在執行流程結束後,如果需要資料回填的話,則進入到 online schema change 流程回填資料然後寫入系統表完成執行流程,如果不需要的話,則在執行運算元的時候就將資料寫入到系統表中完成執行流程。

原文來自:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2901318/,如需轉載,請註明出處,否則將追究法律責任。

相關文章