天天看點

sparksql 中外連接配接查詢中的謂詞下推處理

上月聽了本部門sparksql大牛的sparksql調優分享,當時對一個點不是很了解,回去好好理了一下,整理成文。

基本概念:謂詞下推(predicate pushdown)屬于邏輯優化。優化器可以将謂詞過濾下推到資料源,進而使實體執行跳過無關資料。在使用Parquet或者orcfile的情況下,更可能存在檔案被整塊跳過的情況,同時系統還通過字典編碼把字元串對比轉換為開銷更小的整數對比。

說白了,就是把查詢相關的條件下推到資料源進行提前的過濾操作,之是以這裡說是查詢相關的條件,而不直接說是where 後的條件,是因為sql語句中除了where後的有條件外,join時也有條件。本文讨論的主要就是join時的條件的處理。

sparksql 中外連接配接查詢中的謂詞下推處理

那麼這兩類不同的條件,在外連接配接查詢中是否都會下推呢?不是的,是否下推是遵循一定規則的,對于左連接配接查詢,可以歸納為下表:

左表

右表

Join中條件

不下推

下推

Join後條件

帽子很高,其實就是對2中表格中的規則一個一個來分析。

3.1. 左表join後條件下推

查詢語句如下:

sparksql 中外連接配接查詢中的謂詞下推處理

表結構和資料如下:

左表:

Id

value

1

one

2

two

右表:

id

來分析一下LT.id>1下推到左表左表進行資料過濾的結果:

經過LT.id>1過濾後,左表變為:

此時再和右表進行左連接配接,流程如下:

左表id為2的行,在右表中能join上,則連接配接結果如下:

LT.id

LT.value

RT.value

可見,條件下推過濾了左表整整50%的資料,相當牛叉,雖然隻有兩條。究其原因,是因為在sparksql中,把以上的查詢解析成了如下的子查詢:

sparksql 中外連接配接查詢中的謂詞下推處理

3.2. 左表join中條件不下推

sparksql 中外連接配接查詢中的謂詞下推處理

來看看不下推的情況下計算出的正确結果,join過程如下:

第一步:左表id為1的行在右表中能找到相等的id,但是左表的id為1,是不滿足第二個join條件的,是以左表這一條相當于沒有和右表join上,是以左表的值value保留,而右表的value為null。

第二步:左表id為2 的行在右表中能找到,而且左表id為2的行的id大于1,兩個join條件都滿足,是以算是和右表join上了,是以左表和右表的value都保留。

null

3.3. 右表join中條件下推

sparksql 中外連接配接查詢中的謂詞下推處理

現在把RT.id>1這個右表join中條件下推,來過濾右表,過濾後如下:

然後左表再和右表進行左連接配接,流程如下:

第一步:左表id為1的行在右表中沒有,此時左表值保留,右表為null

第二步:左表id位2的行在右表中有,并且RT.id大于1,兩個join條件都滿足,則左表和右表的值都保留。

那麼如果不下推,來看看結果,流程如下:

第一步:左表id為1的行在右表中有,但是不滿足第二個join條件,是以這行算是沒join上,是以左表資料保留,右表為null

第二步:左表id為2的行在右表中有,也滿足第二個join條件,是以左右表的資料都保留。

wo

可見,右表join中條件下推不下推,結果一樣,是以,幹嗎不下推?可以過濾掉一半的資料呢。Sparksql中的等價處理語句是:

sparksql 中外連接配接查詢中的謂詞下推處理

這個應該是最違反正常了解的查詢了,查詢語句如下:

sparksql 中外連接配接查詢中的謂詞下推處理

首先來看,join後條件不下推的情況,流程如下:

第一步:左表id為1的行在右表中可以找到,但是此時僅僅滿足join條件,在使用where條件判斷這條連接配接後資料時,發現右表的id不滿足RT.id>1的條件,是以這條join結果不保留(注意,這裡是不保留,全都不保留,左表右表都不保留,要跟上邊的沒join上,右表的值為null的情況差別開,這也是關鍵所在)

第二步:左表id為2的行和右表id為2的行join上了,同時也滿足RT.id>1的where條件。

很明顯,這是一條符合語義的正确的查詢結果。

好了,接下來看看右表join後條件下推的情況:

第一步:使用RT.id>1過濾右表,過濾後右表隻剩一行id為2的行

第二步:左表id為1的行在右表中沒有,此時左表值保留,右表值為null

第三步:左表id為2的行在右表中有,此時左表值保留,右表值也保留。

結果如下:

這其實是一個錯誤的結果。

好了分析結束,其實大家也看出來了,我是打着sparksql的幌子騙點選的,任何資料庫其實都會按照這個規則處理的,不是sparksql所特有的。