手把手教你 Spark 效能調優

2021-09-11 13:36:09 字數 3732 閱讀 8905

上周四接到反饋,集群部分 spark 任務執行很慢,且經常出錯,引數改來改去怎麼都無法優化其效能和解決頻繁隨機報錯的問題。

看了下任務的歷史運**況,平均時間 3h 左右,而且極其不穩定,偶爾還會報錯:

任務的執行時間跟什麼有關?

(1)資料來源大小差異

(2)**本身邏輯缺陷

比如**裡重複建立、初始化變數、環境、rdd資源等,隨意持久化資料等,大量使用 shuffle 運算元等,比如reducebykey、join等運算元。

在這份100行的**裡,一共有 3 次 shuffle 操作,任務被 spark driver 切分成了 4 個 stage 序列執行,**位置如下:

咱們需要做的就是從演算法和業務角度盡可能減少 shuffle 和 stage,提公升平行計算效能,這塊是個大的話題,本次不展開詳述。

(3)引數設定不合理

這塊技巧相對通用,咱們來看看之前的核心引數設定:

num-executors=10 || 20 ,executor-cores=1 || 2, executor-memory= 10 || 20,driver-memory=20,spark.default.parallelism=64

假設咱們的 spark 佇列資源情況如下:

memory=1t,cores=400

引數怎麼設定在這裡就有些技巧了,首先得明白 spark 資源的分配和使用原理:

在預設的非動態資源分配場景下, spark 是預申請資源,任務還沒起跑就獨佔資源,一直到整個 job 所有 task 結束,比如你跳板機起了乙個 spark-shell 一直沒退出,也沒執行任務,那也會一直占有所有申請的資源。(如果設定了 num-executors,動態資源分配會失效)

注意上面這句話,spark 的資源使用分配方式和 mapreduce/hive 是有很大差別的,如果不理解這個問題就會在引數設定上引發其它問題。

比如 executor-cores 設多少合適?少了任務並行度不行,多了會把整個佇列資源獨佔耗光,其他同學的任務都無法執行,比如上面那個任務,在 num-executors=20 executor-cores=1 executor-memory= 10 的情況下,會獨佔20個cores,200g記憶體,一直持續3個小時。

那針對本case中的任務,結合咱們現有的資源,如何設定這 5 個核心引數呢?

1) executor_cores*num_executors 不宜太小或太大!一般不超過總隊列 cores 的 25%,比如佇列總 cores 400,最大不要超過100,最小不建議低於 40,除非日誌量很小。

2) executor_cores 不宜為1!否則 work 程序中線程數過少,一般 2~4 為宜。

3) executor_memory 一般 6~10g 為宜,最大不超過 20g,否則會導致 gc 代價過高,或資源浪費嚴重。

4) spark_parallelism 一般為 executor_cores*num_executors 的 1~4 倍,系統預設值 64,不設定的話會導致 task 很多的時候被分批序列執行,或大量 cores 空閒,資源浪費嚴重。

5) driver-memory 早前有同學設定 20g,其實 driver 不做任何計算和儲存,只是下發任務與yarn資源管理器和task互動,除非你是 spark-shell,否則一般 1-2g 就夠了。

spark memory manager:

6)spark.shuffle.memoryfraction(預設 0.2) ,也叫 executionmemory。這片記憶體區域是為了解決 shuffles,joins, sorts and aggregations 過程中為了避免頻繁io需要的buffer。如果你的程式有大量這類操作可以適當調高。

7)spark.storage.memoryfraction(預設0.6),也叫 storagememory。這片記憶體區域是為了解決 block cache(就是你顯示呼叫dd.cache, rdd.persist等方法), 還有就是broadcasts,以及task results的儲存。可以通過引數,如果你大量呼叫了持久化操作或廣播變數,那可以適當調高它。

8)othermemory,給系統預留的,因為程式本身執行也是需要記憶體的, ​(預設為0.2)。other memory在1.6也做了調整,保證至少有300m可用。你也可以手動設定 spark.testing.reservedmemory . 然後把實際可用記憶體減去這個reservedmemory得到 usablememory。 executionmemory 和 storagememory 會共享usablememory * 0.75的記憶體。0.75可以通過 新引數 spark.memory.fraction 設定。目前spark.memory.storagefraction 預設值是0.5,所以executionmemory,storagememory預設情況是均分上面提到的可用記憶體的。

例如,如果需要載入大的字典檔案,可以增大executor中 storagememory 的大小,這樣就可以避免全域性字典換入換出,減少gc,在這種情況下,我們相當於用記憶體資源來換取了執行效率。

最終優化後的引數如下:

效果如下:

(4)通過執行日誌分析效能瓶頸

最後的任務還需要乙個小時,那這乙個小時究竟耗在哪了?按我的經驗和理解,一般單天的資料如果不是太大,不涉及複雜迭代計算,不應該超過半小時才對。

由於集群的 spark history server 還沒安裝除錯好,沒法通過 spark web ui 檢視歷史任務的視覺化執行細節,所以我寫了個小指令碼分析了下前後具體的計算耗時資訊,可以一目了然的看到是哪個 stage 的問題,有針對性的優化。

可以看到優化後的瓶頸主要在最後寫 redis 的階段,要把 60g 的資料,25億條結果寫入 redis,這對 redis 來說是個挑戰,這個就只能從寫入資料量和 kv 資料庫選型兩個角度來優化了。

(5)其它優化角度 當然,優化和高效能是個很泛、很有挑戰的話題,除了前面提到的**、引數層面,還有怎樣防止或減少資料傾斜等,這都需要針對具體的場景和日誌來分析,此處也不展開。

對於初學者來說 spark 貌似無所不能而且高效能,甚至在某些部落格、技術人眼裡 spark 取代 mapreduce、hive、storm 分分鐘的事情,是大資料批處理、機器學習、實時處理等領域的銀彈。但事實確實如此嗎?

從上面這個 case 可以看到,會用 spark、會調 api 和能用好 spark,用的恰到好處是兩碼事,這要求咱們不僅了解其原理,還要了解業務場景,將合適的技術方案、工具和合適的業務場景結合——這世上本就不存在什麼銀彈。。。

說道 spark 的效能,想要它快,就得充分利用好系統資源,尤其是記憶體和cpu:核心思想就是能用記憶體 cache 就別 spill 落磁碟,cpu 能並行就別序列,資料能 local 就別 shuffle。

別手握屠龍寶刀,卻用來切水果,還嫌不利索。:)

[1] spark 記憶體管理

zhangyi.gitbooks.io/spark-in-ac…

[2] spark memory解析

[3] spark1.6記憶體管理模型設計稿-翻譯

ju.outofmemory.cn/entry/24071…

[4] spark記憶體管理

blog.csdn.net/vegetable_b…

[5] apache spark 記憶體管理詳解

[6] spark你一定學得會(四)no.68

bit.ly/2yur2mz

[7] 想要理解 spark rdd 就自己寫乙個

bit.ly/2zgei03

手把手教你OA選型

oa選型永遠是oa行業的重要焦點,在選型問題上困擾了很多客戶,雲全oa從這幾個方面教你如何選型。了解研發技術 技術是硬道理。只有過關的技術才會研發出過硬的產品。如果技術不過關,後期將會帶來一系列的問題。同時需要考慮技術的先進性。在現如今社會發展日新月異,今天還遙遙領先的管理模式也許明天就會被淘汰。所...

手把手教你玩轉git

使用以上來寫git命令。mkdir xx 建立乙個空目錄 xx指目錄名 pwd 顯示當前目錄的路徑。git init 把當前的目錄變成可以管理的git倉庫,生成隱藏.git檔案。git add xx 把xx檔案新增到暫存區去。git commit m xx 提交檔案 m 後面的是注釋。git sta...

npm發布 手把手教你

註冊完畢,回到我們的電腦。windows直接cmd到命令列 輸入以下命令,會提示輸入使用者名稱 密碼 郵箱,這些都是註冊時填寫過的。發布之前首先準備 手動建立乙個命名為lib的資料夾,手動建立index.js package.sjon和typing.d.ts文件,建立完成之後就開始下一步,向裡面新增...