本文介紹ApsaraDB for SelectDB中針對Join操作所設計的一系列最佳化方案,以及基於此給出的Join最佳化參考,協助您提升查詢速度。
物理運算元支援
SelectDB支援如下兩種物理Join運算元,用於實現單機引擎中資料進行Join的處理過程。
Hash Join:在右表上根據等值Join列建立雜湊表,左表流式的利用雜湊表進行Join計算,這個運算元只適用於等值Join。
Nest Loop Join:通過兩個for迴圈進行Join過程處理。它適用的情境是不等值的Join,例如大於小於或者是需要求笛卡爾積的情境。它是一個通用的Join運算元,但是效能表現差。
Shuffle方式概述
作為分布式的MPP資料庫,SelectDB在Join的過程中需要先進行資料的Shuffle,然後才調用物理運算元進行處理。SelectDB現階段支援4種Shuffle方式,以下舉例說明。
下述樣本將對錶S和表R進行Join,其中N表示參與Join計算的節點的數量,T則表示表的記錄數。
Broadcast Join
它要求把R表的全量的資料都發送到S表上,即每一個參與Join的節點,它都擁有R表全量的資料,也就是
T(R)。這個Shuffle方式較為通用,同時能夠支援Hash Join和Nest loop Join。它的網路開銷是N*T(R)。S表資料不移動,R表資料發送到S表資料的掃描節點。
Shuffle Join
當進行Hash Join時,可以通過Join列計算S表和R表相應資料的雜湊值,把相同雜湊值的資料被分發到分布式系統中的同一個節點,利用分布式系統加速Join查詢。它的網路開銷是
T(S)+T(R),但它只能支援Hash Join,因為它根據Join的條件計算分桶。S表和R表資料根據分區計算,計算的結果發送到不同的分區節點上。
Bucket Shuffle Join
SelectDB的表資料本身是通過雜湊計算分桶的,所以就可以利用表本身的分桶列的性質來進行Join資料的Shuffle。例如兩張表;表S和表R需要做Join,並且Join列是表S的分桶列,那麼表S的資料其實可以不需要移動,通過移動分發表R的資料就可以完成Join的計算。
它的網路開銷是
T(R),相當於只Shuffle表R的資料就可以完成Join。有關Bucket Shuffle Join使用的更多細節,詳見Bucket Shuffle Join。表S資料不移動,表R資料根據分區計算的結果發送到S表掃表的節點
Colocation Join
對於多個相關聯的表,在建表時確保表的資料分區數量一致,相同Hash分桶在分布式系統中的分布一致,那麼實際查詢時就可以跳過資料的Shuffle過程,直接進行Join計算,提升查詢效能。Colocation Join詳情,請參見Colocation Join。
資料已經預先分區,不需要考慮網路開銷,直接在本地進行Join計算。
四種Shuffle方式對比如下。
Shuffle方式 | 網路開銷 | 物理運算元 | 適用情境 |
BroadCast | N*T(R) | Hash Join/Nest Loop Join | 通用 |
Shuffle | T(S)+T(R) | Hash Join | 通用 |
Bucket Shuffle | T(R) | Hash Join | Join條件中存在左表的分布式列,且左表在執行時只使用單分區的資料 |
Colocation | 0 | Hash Join | Join條件中存在左表的分布式列,且左右表同屬於一個Colocate Group |
上述這4種Shuffle方式的靈活度按照從高到低排列,對資料分布的要求越來越嚴格,但Join計算的效能也逐級提高。
Runtime Filter最佳化
Runtime Filter在查詢規劃時動態產生,由HashJoin運算元(對應查詢Explain/Profile中的HashJoinNode)中將Join過程中的右錶轉換為過濾條件,下推給資料掃描運算元(對應查詢Explain/Profile中的ScanNode),然後在左表掃描過程中進行裁剪過濾。這種方式大幅降低查詢過程中的資料讀取和計算,提升了查詢效能。Runtime Filter詳情,請參見Runtime Filter。
Join Reorder
在多表Join的情境下,Join的順序對整個Join查詢的效能影響很大。
例如有三張表Join,如下圖所示。其中ScanA,ScanB,ScanC代表表A,B,C根據查詢條件進行完初步的Scan後得到的資料。
在上圖的左側,表A和表B的Scan先進行Join,產生2000行中間結果,然後與表C的Scan再進行Join計算。
在右側,Join的順序經過了調整。把表A的Scan先與表C的Scan Join,產生的中間結果只有100,然後再與表B的Scan Join計算。最終的Join結果是一樣的,但是它產生的中間結果有20倍的差距,因此會產生一個很大的效能差距。
SelectDB目前支援基於規則的Join Reorder演算法。它的邏輯如下。
建議優先選擇將大表與小表進行Join操作,以便產生儘可能小的中間結果。
將有條件的Join表寫在查詢語句中的靠前位置,盡量讓有條件的Join表進行過濾。
Hash Join的優先順序高於Nest Loop Join,因為Hash Join的執行速度明顯快於Nest Loop Join。
Join調優方法
Join調優的方法大致按照如下步驟進行。
利用SelectDB本身提供的Profile來定位查詢的瓶頸。Profile會記錄SelectDB整個查詢中的各種資訊,這對進行效能調優非常重要。
深入瞭解SelectDB的Join機制,瞭解其原理,才能深刻分析其效能較慢的原因。
利用會話變數來修改Join操作的一些行為,以實現Join操作的最佳化。
查看Query Plan去分析這個調優是否生效。
上述4個步驟描述了標準的Join調優流程。如果在完成了上述流程後仍未見效果,可能需要重新編寫Join語句,或者調整資料分布並重新檢查整個資料分布是否合理,包括手動調整查詢所用的Join語句。然而,這種方式的所花費的成本相對較高,因此在上述方法無效的情況下才需要進一步分析。
Join調優建議
SelectDB Join最佳化調優的一些建議如下。
進行Join的時候,盡量選擇同類型或者簡單類型的列,同類型列可以減少資料Cast,簡單類型本身Join計算就很快。
盡量選擇Key列進行Join,原因參見Runtime Filter。Key列在延遲物化上有較好的效果。
盡量讓大表之間的Join以Colocation的方式進行。因為大表之間進行Join會帶來很大的網路開銷,會使得Shuffle的代價急劇升高。
合理使用Runtime Filter。它在Join過濾率高的情境下效果非常顯著,但它同時具有一定副作用,需要根據具體的SQL的粒度做開關。
涉及到多表Join時,需要去判斷Join的合理性。盡量保證左表為大表,右表為小表。這種情境下,Hash Join會優於Nest Loop Join。必要的時可以通過SQL Rewrite,利用Hint去調整Join的順序。