本文分享自華爲雲社區《GaussDB(DWS)向量化執行引擎詳解》,作者: yd_212508532。
前言
- 適用版本:【基線功能】
傳統的行執行引擎大多采用一次一元組的執行模式,這樣在執行過程中CPU大部分時間並沒有用來處理數據,更多的是在遍歷執行樹,就會導致CPU的有效利用率較低。而在面對OLAP場景巨量的函數調用次數,需要巨大的開銷。爲了解決這一問題,GaussDB(DWS)中增加了向量化引擎。向量化引擎使用了一次一批元組的執行模式,能夠大大減少遍歷執行節點的開銷。同時向量化引擎還天然對接列存儲,能夠較爲方便地在底層掃描節點裝填向量化的列數據。列存 + 向量化執行引擎,是打開OLAP性能之門的金鑰匙之一!
關於行存、列存表
行存表按行存儲tuple到Page頁面。多用於TP場景,這些場景數據頻繁更新,增刪改操作多,查詢結果涉及表的多列。
列存表按列存儲,每列數據存儲到一個文件。多用於AP場景。
- 表列數多,訪問列數少,減少IO操作次數
- 列數據具有同質性,提高數據壓縮比
- 基於列批量數據的運算,CPU的cache命中率高
執行框架
執行器是優化器與存儲引擎的交互樞紐。以優化器生成的執行計劃樹爲輸入,從存儲引擎訪問數據,並按照計劃,操作各種執行算子,從而實現數據的處理。採用Pipeline模式, 行執行器一次一tuple,列執行器一次一batch。上層驅動下層,使得數據在執行樹上流動。提供各種數據處理的執行算子。下圖展示了自上而下的控制流和自下而上的數據流。
執行器的執行過程可分爲這三個步驟:
- 執行器初始化:構造執行器全局狀態信息estate、遞歸遍歷計劃樹各節點,初始化其執行狀態信息planstate
- 執行器的執行:行引擎和向量化引擎入口獨立開,從計劃樹根節點開始,遞歸遍歷到葉節點獲取一個tuple/batch,經過逐層節點算子的處理,返回一個結果tuple/batch,直到再無tuple/batch。
- 執行器的清理:回收執行器全局狀態信息,清理各plan node的執行狀態。
列執行器
行執行器的問題是:CPU大部分處理在遍歷Plan Tree過程,而不是真正處理數據,CPU有效利用率低。列存表獨有的應用場景,需要配套的向量化引擎,才能真正發揮其在OLAP場景下提升性能的優勢。因此,列執行器的改造基本思路爲:一次處理一列數據。
和行執行器一樣,向量化執行引擎調度器,遵循Pipeline模式,但每次處理及在算子間傳遞數據爲一次一個Batch(即1000行數據),CPU命中率提高,IO讀操作減少。列執行器的數據流結構VectorBatch如下圖所示。
行列混合:Adapter算子
列存表的某些場景不支持向量化執行引擎,譬如:string_to_array、listagg、string_agg等。
GaussDB具有將兩套行列引擎自動切換的能力。
針對列存數據,如果只有行引擎,通常需要將列數據重構成元組tuple給執行引擎逐行處理。Tuple deform過程影響列存數據查詢處理的性能。
向量化執行引擎的性能
對比行列存引擎對同一表達式x*(1-y)計算的性能,可以看到列存引擎的Cstore Scan算子相比行存引擎的Seq Scan算子,耗時減少了85%。
向量計算的特點是:一次計算多個值,減少函數調用和上下文切換,儘量利用CPU的緩存以及向量化執行指令提高性能。
向量化執行引擎的性能優勢:
- 一次一Batch,讀取更多數據,減少IO讀次數
- 由於Batch中記錄數多,相應的CPU的cache命中率提升
- Pipeline模式執行過程中的函數調用次數減少
- 與列存表配套,減少tuple deform,即列存數據重構tuple的時間開銷
行/列執行器各算子對照
向量化引擎的執行算子類似於行執行引擎,包含控制算子、掃描算子、物化算子和連接算子。同樣會使用節點表示,繼承於行執行節點,執行流程採用遞歸方式。主要包含的節點有:CStoreScan(順序掃描),CStoreIndexScan(索引掃描),CStoreIndexHeapScan(利用Bitmap獲取元組),VecMaterial(物化),VecSort(排序),VecHashJoin(向量化哈希連接)等,下面將逐一介紹這些執行算子。
掃描算子
掃描算子用來掃描表中的數據,每次獲取一條元組作爲上層節點的輸入, 存在於查詢計劃樹的葉子節點,它不僅可以掃描表,還可以掃描函數的結果集、鏈表結構、子查詢結果集。一些比較常見的掃描算子如表所示。
算子(行/列存算子) | 含義 | 出現場景 |
---|---|---|
SeqScan/ CStoreScan | 順序掃描 | 最基本的掃描算子,用於掃描物理表(沒有索引輔助的順序掃描) |
IndexScan/CStoreIndexScan | 索引掃描 | 選擇條件涉及的屬性上建立了索引 |
IndexOnlyScan/CStoreIndexOnlyScan | 直接從索引返回元組 | 索引列完全覆蓋結果集列 |
BitmapScan(BitmapIndexScan, BitmapHeapScan) / CStoreIndexHeapScan (CStoreIndexAnd, CStoreIndexOr,CStoreIndexCtidScan) | 利用Bitmap獲取元組 | BitmapIndexScan利用屬性上的索引進行掃描,返回結果爲一個位圖;BitmapHeapScan從BitmapIndexScan輸出的位圖中獲取元組 |
TidScan | 通過元組tid獲取元組 | 1.WHERE conditions(like CTID = tid or CTID IN (tid1, tid2, …)) ;2.UPDATE/DELETE … WHERE CURRENT OF cursor |
SubqueryScan/VecSubqueryScan | 子查詢掃描 | 以另一個查詢計劃樹(子計劃)爲掃描對象進行元組的掃描 |
FunctionScan | 函數掃描 | FROM function_name |
ValuesScan | 掃描values鏈表 | 對VALUES子句給出的元組集合進行掃描 |
ForeignScan/VecForeignScan | 外部表掃描 | 查詢外部表 |
CteScan/VecCteScan | CTE表掃描 | 掃描SELECT查詢中用WITH子句定義的子查詢 |
連接算子
連接算子對應了關係代數中的連接操作,以表 t1 join t2 爲例,主要的集中連接類型如下:inner join、left join、right join、full join、semi join、 anti join,其實現方式包括Nestloop、HashJoin、MergeJoin;
算子(行/列存算子) | 含義 | 出現場景 |
---|---|---|
NestLoop/VecNestLoop | 嵌套循環連接,暴力連接,對每一行都掃描內表 | Inner Join, Left Outer Join, Semi Join, Anti Join |
MergeJoin/VecMergeJoin | 歸併連接(輸入有序),內外表排序,定位首尾兩端,一次性連接元組。等值連接 | Inner Join, Left Outer Join, Right Outer Join, Full Outer Join, Semi Join, Anti Join |
HashJoin/VecHashjoin | 哈希連接,內外表使用join列的hash值建立hash表,相同值的必在同一個hash桶。等值連接 | Inner Join, Left Outer Join, Right Outer Join, Full Outer Join, Semi Join, Anti Join |
物化算子
物化算子是一類可緩存元組的節點。在執行過程中,很多擴展的物理操作符需要首先獲取所有的元組才能進行操作(例如聚集函數操作、沒有索引輔助的排序等),這是要用物化算子將元組緩存起來;
算子(行/列存算子) | 含義 | 出現場景 |
---|---|---|
Material/VecMaterial | 物化 | 緩存子節點結果 |
Sort/VecSort | 排序 | ORDER BY子句,連接操作,分組操作,集合操作,配合Unique |
Group/VecGroup | 分組操作 | GROUP BY子句 |
Agg/VecAggregation | 執行聚集函數 | 1. COUNT/SUM/AVG/MAX/MIN等聚集函數;2. DISTINCT子句;3. UNION去重;4. GROUP BY子句 |
WindowAgg/VecWindowAgg | 窗口函數 | WINDOW子句 |
Unique/VecUnique | 去重(下層已排序) | 1. DISTINCT子句;2. UNION去重 |
Hash | HashJoin輔助節點 | 構造hash表,配合HashJoin |
SetOp/VecSetOp | 處理集合操作 | INTERSECT/INTERSECT ALL, EXCEPT/EXCEPT ALL |
LockRows | 處理行級鎖 | SELECT … FOR SHARE/UPDATE |
控制算子
控制算子是一類用於處理特殊情況的節點,用於實現特殊的執行流程。
算子(行/列存算子) | 含義 | 出現場景 |
---|---|---|
Result/VecResult | 直接進行計算 | 1. 不包含表掃描;2. INSERT語句中只有一個VALUES子句;3. 當 Append/MergeAppend爲計劃根節點(投影上推) |
ModifyTable | INSERT/UPDATE/DELETE上層節點 | INSERT/UPDATE/DELETE |
Append/VecAppend | 追加 | 1. UNION(ALL);2. 繼承表 |
MergeAppend | 追加(輸入有序) | 1. UNION(ALL);2. 繼承表 |
RecursiveUnion | 處理WITH子句中遞歸定義的UNION子查詢 | WITH RECURSIVE … SELECT … 語句 |
BitmapAnd | Bitmap邏輯與操作 | 多維索引掃描的BitmapScan |
BitmapOr | Bitmap邏輯或操作 | 多維索引掃描的BitmapScan |
Limit/VecLimit | 處理LIMIT子句 | OFFSET … LIMIT … |
其他算子
其他算子包括Stream算子,以及RemoteQuery等算子
算子(行/列存算子) | 含義 | 出現場景 |
---|---|---|
Stream | 多節點數據交換 | 執行分佈式查詢計劃,節點間存在數據交換 |
Partition Iterator | 分區迭代器 | 分區表掃描,迭代掃描每個分區 |
VecToRow/RowToVec | 列轉行/行轉列 | 行列混合場景 |
DfsScan / DfsIndexScan | HDFS表(索引)掃描 | HDFS表掃描 |
Gaussdb向量化的演進
在第一代向量化引擎之後,GaussDB演化出具有更高性能的向量化引擎:Sonic向量化引擎和Turbo向量化引擎。
GaussDB爲了OLAP執行性能提升,在列存 + 向量化執行引擎、批量計算的路上不斷演進:
- Stream算子 + 分佈式執行框架,支持數據在多節點間流動
- SMP,節點內多線程並行,充分利用空閒硬件資源
- LLVM技術,全新的代碼生成框架,JIT(just in time)編譯器,消除tuple deform瓶頸
- Sonic向量化引擎,對HashAgg、HashJoin算子進一步向量化,根據每列不同類型實現不同Array來對數據做計算
- 新一代Turbo向量化引擎,對大部分算子做進一步向量化,在Sonic引擎的基礎上,新增了Null優化、大整數優化、Stream優化、Sort優化等,進一步提升了性能
總結
本文介紹了GaussDB向量化執行引擎,對其框架、原理、各算子概況、性能提升等做了詳細闡述。