一、Join 邏輯計劃生成
和 Join 相關的邏輯層的優化規則主要包含以下幾種:
ReorderJoin
EliminateOuterJoin
以及
中和 Join 相關的 predicate pushDown
二、Join 物理計劃生成和選取
2.1、基本概念
在 Spark SQL 中,參與 Join 操作的兩張表分別被稱為流式表(StreamTable)和構件表(BuildTable),不同表的角色在 Spark SQL 中會通過一定的策略進行設定。通常來講,系統會將大表設置為 StreamTable,小表設置為 BuildTable。流式表的迭代器為 streamIter,構建表的迭代器為 buildIter。遍歷 streamIter 的每一條記錄,然后在 buildIter 中查找匹配的記錄。這個查找過程稱為 build 過程。每次 build 操作的結果為一條 JoinedRow(A, B)
,其中 A 來自 streamedIter,B 來自 buildIter。
再例如,在 BroadcastHashJoin 中需要決定廣播哪個數據表。這里的 BuildSide 可以簡單理解為 “構建的一邊”。
在 Spark 中,BuildSide 作為一個抽象類,包含 BuildLeft 和 BuildRight 兩個子類,一般在構造 Join 的執行算子時,都會傳入一個 BuildSide 的構造參數。在 JoinSelection 中通過 canBuildRight
和 canBuildLeft
判斷一個 Join 類型能否 “構建” 右表和左表。
2.2、物理計劃選取順序
Join 物理執行計劃的選取在 JoinSelection 中進行,其主要邏輯如下:
如果是一個等值 join(equi-join)且包含 join hint,我們依次查看 join hint:
-
broadcast hint
:如果 join 類型支持,使用 broadcast hash join。如果 left 和 right 都有 broadcast hint,選擇 size 較小的一側(基于統計數據)進行 broadcast -
sort merge hint
:如果 join keys 是可排序的,使用 sort merge join。 -
shuffle hash hint
:如果 join 類型支持,如果 left 和 right 都設置了 shuffle hash hints,選擇 size 較小的一側作為 build side -
shuffle replicate NL hint
:如果 join type 為 inner like,使用 cartesian product join(笛卡爾積)
JoinSelection 通過 ExtractEquiJoinKeys
來判斷是否為等值 Join 并提取相關信息:
如果沒有指定 hint 或 hint 不適用,Join 選擇順序如下:
- 嘗試選用 broadcast hash join:如果 join 類型支持,且 join 的一側 size 足夠小能夠 broadcast。如果都足夠小,選擇更小的一側進行 broadcast(基于統計數據)
- 嘗試選用 shuffle hash join:如果 join 類型支持,且 join 的一側 size 足夠小能夠構建 local hash map,且該側 size 顯著小于另一側,且
spark.sql.join.preferSortMergeJoin
為 false - 嘗試選用 sort merge join:如果 join keys 是可排序的
- 嘗試選用笛卡爾積:如果是
inner like join
- 嘗試選用 broadcast nested loop join:最后的兜底手段,可能會 OOM,如果這里 OOM 了,也沒辦法了
2.3、等值 Join 情況
注①:
createJoinWithoutHint 如下: