您好,登錄后才能下訂單哦!
怎么實現Spark性能的調優,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
0、背景
集群部分 spark 任務執行很慢,且經常出錯,參數改來改去怎么都無法優化其性能和解決頻繁隨機報錯的問題。
看了下任務的歷史運行情況,平均時間 3h 左右,而且極其不穩定,偶爾還會報錯:
1、優化思路
任務的運行時間跟什么有關?
(1)數據源大小差異
在有限的計算下,job的運行時長和數據量大小正相關,在本例中,數據量大小基本穩定,可以排除是日志量級波動導致的問題:
(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)其它優化角度
當然,優化和高性能是個很泛、很有挑戰的話題,除了前面提到的代碼、參數層面,還有怎樣防止或減少數據傾斜等,這都需要針對具體的場景和日志來分析,此處也不展開。
2、spark 初學者的一些誤區
對于初學者來說 spark 貌似無所不能而且高性能,甚至在某些博客、技術人眼里 spark 取代 mapreduce、hive、storm 分分鐘的事情,是大數據批處理、機器學習、實時處理等領域的銀彈。但事實確實如此嗎?
從上面這個 case 可以看到,會用 spark、會調 API 和能用好 spark,用的恰到好處是兩碼事,這要求咱們不僅了解其原理,還要了解業務場景,將合適的技術方案、工具和合適的業務場景結合——這世上本就不存在什么銀彈。
說道 spark 的性能,想要它快,就得充分利用好系統資源,尤其是內存和CPU:核心思想就是能用內存 cache 就別 spill 落磁盤,CPU 能并行就別串行,數據能 local 就別 shuffle。
看完上述內容,你們掌握怎么實現Spark性能的調優的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。