Spark SQL 自適應執行優化引擎

在本篇文章中,筆者將給大家帶來 Spark SQL 中關於自適應執行引擎(Spark Adaptive Execution)的內容。

在之前的文章中,筆者介紹過 Flink SQL,目前 Flink 社區在積極地更新迭代 Flink SQL 功能和優化性能,尤其 Flink 1.10.0 版本的發佈,在增強流式 SQL 處理能力的同時也具備了成熟的批處理能力。但是在 SQL 功能完整性和生產環境的實踐應用等方面,Spark SQL 還是更勝一籌,至於 SQL 批處理方面性能優劣,則需要筆者親自去實踐。

不過,在超大規模集羣和海量數據集上,Spark SQL 目前仍然在穩定性和性能方面遇到一些挑戰。爲了應對這些挑戰,Spark 社區進行了改進並引入了自適應執行引擎,它可以在運行時動態地處理任務並行度、join 策略優化和數據傾斜,確保使用運行時統計信息選擇最佳執行計劃。筆者參考 Haifeng Chen 分享的主題《 Spark Adaptive Execution Unleash the Power of Spark SQL 》,再結合實際情況進行梳理。

挑戰

我們首先來看一下,Spark SQL 在實際生產案例中遇到的一些挑戰。

挑戰 1:並行度問題

在日常的 Spark SQL 開發中,我們通過設置 spark.sql.shuffle.partitions 參數來調整 partition 數量,默認值是200。即 Shuffle partition 數量需要手動調整纔可以獲得相對理想的性能。

雖然我們可以設置 shuffle partition 數量,但是無法給出一個對所有任務來說都是最優的值,因爲每個 task 處理的的數據量以及 shuffle 策略也可能不同。

Shuffle partition 太大或太小都會帶來問題:

  • partition 數量太大

    可能會需要處理大量小的 task,導致增加 task 調度開銷以及資源調度開銷。另外,如果該 Stage 最後要輸出存儲,造成很多小的 IO 操作,還會造成在 HDFS 上存儲大量的小文件。

  • partition 數量太小

    可能會導致每個 task 處理大量的數據,處理效率低下,無法有效利用集羣資源的並行處理能力,甚至導致 OOM 的問題。

目前 shuffle partition 數量無法根據每個任務動態調整,只能針對不同的任務進行多次的優化調整,才能得到較爲合理的值,但是往往作業的數據量是逐日累增的,所以之前優化的值可能不再適合後續的作業。

因此理想情況下,爲了獲取最佳的性能,Spark 能夠實現在作業執行過程中根據數據量大小動態設置合適的 shuffle partition 數量。

總結一下並行度問題帶來的挑戰:

  • 數據規模是動態變化的,很難準確評估

  • 單一的 partition 配置不可能適合所有的 partition 以獲得最佳性能

挑戰 2:Join 策略選擇問題

針對不同數據量大小的場景,Spark 支持三種 join 策略以獲取最佳的性能:

  • Sort Merge Join

  • Shuffle Hash Join

  • Broadcast Hash Join

既然 Spark 有三種 join 策略,那麼實際會帶來哪些挑戰:

  • join 策略的選擇是基於靜態信息的,比如執行計劃階段的表大小

  • 對於複雜查詢,中間操作的結果集數據大小變化頻繁,很難評估

因此,很多時候,運行的作業可能沒有選擇最有效的 join 執行策略。

挑戰 3:數據傾斜

數據傾斜是指某一個 partition 的數據量遠遠大於其它 partition 的數據,導致該任務的運行時間遠遠大於其它任務,因此導致整個 SQL 的運行效率變差。

我們使用的 MapReduce、Spark 和 Flink 都會存在數據傾斜的問題,而且在實際需求開發中(比如使用 join 和 group by 操作),數據傾斜問題也是出現頻率比較高的,大部分作業卡在 99% 進度的罪魁禍首。

數據傾斜引起的原因很多,比如:

  • 源表本身就有傾斜的數據

  • 中間操作(比如 outer join)可能生成傾斜數據

簡單總結一下產生數據傾斜的問題:

  • 通常無法提前預測

  • 作業運行過程被單個 task 拖垮

  • 可能引起 OOM

在 Spark SQL 實踐中,處理數據傾斜的常見手段有:

  • 1. 增加 shuffle partition 數量

    通過調整 shuffle partition 數量來避免某個 partition 數據量特別大,將該 partition 數據分散到多個 partition 中。

  • 2. 加鹽處理傾斜的 key

    增加 shuffle partition 數量的方法,對於同一個海量數據傾斜的 key 來說,不起作用。不過,我們可以對該數據傾斜的 key 通過加鹽方式來打散數據,然後再借助 shuffle partition 的功能。

  • 3. 使用 Broadcast Hash Join

    在某些場景下,可以把 Sort Merge Join 轉化成 Broadcast Hash Join,從而避免 shuffle 產生的數據傾斜。比如,如果兩個 join 的表中有一個表是小表,可以優化成Broadcast Hash Join 來消除 shuffle 引起的數據傾斜問題。

但是上面這些解決方案都是針對單一任務進行調優,沒有一個解決方案可以有效的解決所有的數據傾斜問題。

Spark Adaptive Execution

Spark SQL Execution 介紹

筆者簡單說一下,SQL 語句首先通過 Parser 模塊被解析爲語法樹,稱爲 Unresolved Logical Plan,接着 Unresolved Logical Plan 通過 Analyzer 模塊藉助於 Catalog 中的表信息解析爲 Logical Plan,然後 Optimizer 再通過各種優化策略進行深入優化,得到 Optimized Logical Plan,Planner 模塊再將優化後的邏輯計劃根據預先設定的映射邏輯轉換爲 Physical Plan,最後物理執行計劃做 RDD 計算,提交 Spark 集羣運算,最終向用戶返回數據。

Adaptive Execution 想法

基於社區的工作,Intel 大數據技術團隊創建了 Adaptive Execution 項目,對 Adaptive Execution 做了重新的設計,實現了一個更爲靈活的自適性執行框架,來解決主要的性能問題。

Adaptive Execution 項目的想法是: 

當一個 stage 的 map 任務在 runtime 完成時,我們利用 map 輸出大小信息,對並行度、join 策略和傾斜處理進行相應的調整。

Adaptive Execution 框架

  • 當一個 Adaptive Stage 執行時,它會急切地執行它所有的子 Adaptive Stage

  • 當所有的子 Adaptive Stage 執行完成後,它將擁有所有的 map 輸出大小,用於優化決策。

並行度優化

通過使用 map 輸出大小的信息,我們可以在運行時對並行度進行調整。 

如上圖所示,假設我們設置初始 shuffle partition 數量爲 8,在 map stage 結束之後,可以看到每一個 Partition(1-8)的大小分別是20M、30M、10M、20M、35M、45M、10M 和 70M。假設設置每一個 reducer 處理的目標數據量(target input size)是 64M,那麼在運行時,我們實際使用 4 個 reducer,即第一個 reducer 處理 Partition 1-3,共 60M,第二個 reducer 處理 Partition 4-5,共 55M,第三個 reducer 處理 Partition 6-7,共 55M,第四個 reducer 處理 Partition 8,即 70M。整個作業需要 4 個 task 運行,而不是 8 個 task。

一般情況下,一個 partition 是由一個 task 來處理的。經過優化,我們可以安排一個 task 處理多個 partition,這樣,我們就可以保證各個分區相對均衡,不會存在大量數據量很小的 partition。

開啓 Adaptive Execution 特性的方式:

  • spark.sql.adaptive.enabled=true

配置:

  • spark.sql.adaptive.shuffle.targetPostShuffleInputSize

    動態調整 reduce 個數的 partition 大小依據。如設置 64MB,則 reduce 階段每個 task 最少處理 64MB 的數據。默認值爲 64MB。

  • spark.sql.adaptive.shuffle.targetPostShuffleRowCount

    動態調整 reduce 個數的 partition 條數依據。如設置 20000000,則 reduce 階段每個 task 最少處理 20000000 條的數據。默認值爲 20000000。

  • spark.sql.adaptive.minNumPostShufflePartitions

    reduce 個數區間最小值。

  • spark.sql.adaptive.maxNumPostShufflePartitions

    reduce 個數區間最大值。

Join 策略優化

通過使用 map 輸出大小的信息,我們可以在運行時對 join 策略進行調整。 

在 Shuffle Write 之後,觀察兩個 Stage 輸出的數據量。如果有一個 Stage 數據量明顯比較小,可以轉換成 Broadcast Hash Join,這樣就可以動態的去調整執行計劃。

將 Sort Merge Join 轉化成 Broadcast Hash Join,此時 join 讀取數據是直接從本地讀取,沒有數據通過網絡傳輸,避開了網絡IO的開銷,性能會高很多。

開啓方式:

  • spark.sql.adaptive.enabled=true

  • spark.sql.adaptive.join.enabled=true

配置:

  • spark.sql.adaptiveBroadcastJoinThreshold

    設置了 SortMergeJoin 轉 BroadcastJoin 的閾值。如果不設置該參數,該閾值與 spark.sql.autoBroadcastJoinThreshold 的值相等。

  • 是否允許其他的 shuffle

傾斜數據處理

  • 對於大量小數據的 partiiton,可以通過合併來解決問題,即一個 task 處理多個 partition 的數據。

  • 對於數據量特別大的 partition,使用多個 task 來處理該 partition。

開啓自動調整數據傾斜功能後,在作業執行過程中,Spark 會自動找出出現傾斜的 partiiton,然後用多個 task 來處理該 partition,之後再將這些 task 的處理結果進行合併。

開啓方式:

  • spark.sql.adaptive.skewedJoin.enabled=true

    傾斜處理開關。

  • spark.sql.adaptive.skewedPartitionMaxSplits

    在開啓 Adaptive Execution 時,控制處理一個傾斜 partition 的 task 個數上限,默認值爲 5。

  • spark.sql.adaptive.skewedPartitionRowCountThreshold

    傾斜的 partition 條數不能小於該值。partition 的條數如果少於這個值,數據量再大也不會被當成是傾斜的partition。默認值爲 10000000。

  • spark.sql.adaptive.skewedPartitionSizeThreshold

    傾斜的 partition 大小不能小於該值。默認值爲 64MB。

  • spark.sql.adaptive.skewedPartitionFactor

    當一個 partition 的 size 大小大於該值(所有 parititon 大小的中位數)且大於spark.sql.adaptive.skewedPartitionSizeThreshold,或者 parition 的條數大於該值(所有 parititon 條數的中位數)且大於 spark.sql.adaptive.skewedPartitionRowCountThreshold,纔會被當做傾斜的 partition 進行相應的處理。默認值爲 10。

性能提升

  • TPC-DS 100TB

    大部分查詢性能提升 10% ~ 50%,一些查詢性能提升超過 50%,甚至達到 200% 以上。另外有一些查詢,如果不使用 Adaptive Execution,則無法完成或者失敗,而使用 Adaptive Execution 全部通過測試。

  • Baidu 性能提升分享

    50% ~ 200% 性能提升,大部分通過 sort merge join 轉變爲 broadcast hash join。

  • Alibaba 性能提升分享

    TPC-DS 1TB,總體性能提升 1.38 倍,最大性能達到 3 倍。

  • 國內其他一些公司使用

    對於由 outer join 導致的數據嚴重傾斜的查詢,最高可達 10 倍以上的性能提升。

參考

  • Spark Adaptive Execution Unleash the Power of Spark SQL - Haifeng Chen (Intel)

  • https://github.com/Intel-bigdata/spark-adaptive

  • https://issues.apache.org/jira/browse/SPARK-23128

         

              你若喜歡,點個在看

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章