Spark優化總結(三)——調參

Spark優化總結(三)——調參

前言

  • 不要期待修改一個參數能夠像魔法一樣立馬得到神奇的好效果!(某些時候效果確實很棒^_^)你應當把參數看作一道菜中的調味品,能夠豐富味道,但主要還是得靠原材料的質量與炒菜的技藝。
  • 開發Spark應用時,應當先優化好你的應用代碼,再來思考調參優化(必要的參數的除外)。
  • 調參是一個比較複雜的主題,不同的環境、不同的代碼都會導致同樣的參數產生不同的效果。建議儘量在確定您的生產環境情況後、在優化好存在明顯問題的代碼後,再做調參測試。
  • 下面會列出開發中常用的部分參數,並加以解釋,以作參考

簡單示例

  • 一個Spark任務提交示例

    spark-submit \
    --queue test_queue --master yarn --deploy-mode cluster \
    --num-executors 10 --executor-memory 8G --executor-cores 4 \
    --driver-memory 4G --driver-cores 2 \
    --conf spark.network.timeout=300 \
    --conf spark.locality.wait=9s \
    --class com.skey.spark.app.MyApp /home/jerry/spark-demo.jar 
    
  • 參數解釋

    • –queue test_queue 將任務提交到YARN上面的test_queue隊列
    • –master yarn 將任務運行在YARN上面
    • –deploy-mode cluster 指定cluster模式運行
    • –num-executors 10 指定啓動10個Executor進程
    • –executor-memory 8G 每個Executor分配8G內存
    • –executor-cores 4 每個Executor分配4個core,一個core對應一個線程
    • –driver-memory 4G 爲Driver進程分配4G內存
    • –driver-cores 2 爲Driver分配2個core,一個core對應一個線程
    • –conf spark.network.timeout=300s 設置集羣內網絡通信延遲爲300s
    • –conf spark.locality.wait=9s 設置數據本地化加載等待時間
    • –class com.skey.spark.app.MyApp 指定需要運行的spark應用類
    • /home/jerry/spark-demo.jar 指定需要運行的jar包

常用參數

  • 下面列出的參數以Spark 2.4.3的configuration爲標準
  • spark.memory.useLegacyMode
    • 默認值: false
    • 解釋: 該參數用於控制是否使用遺留的內存管理模式(也就是鎢絲計劃之前的內存模型,即1.6版本以前)。遺留模式下,內存由spark.storage.memoryFraction(0.6) + spark.shuffle.memoryFraction(0.2) + 系統默認(0.2) 組成。
    • 建議: 剛開始開發應用時,可以不開啓,直接動態分配即可。一旦開發完成,如果能夠確定每次應用的shuffle、storage量時,可以嘗試改爲true,這樣的話不用動態分配內存(需要耗時)。另外動態分配模式下execution從storage借來的內存不管還需不需要用都不會還給storage,可能會導致部分問題。
  • spark.storage.memoryFraction
    • 默認值: 0.6
    • 解釋: spark.memory.useLegacyMode爲true時有效,用於控制緩存部分的內存
    • 建議: 如果緩存數據比較大,可以調大該參數
  • spark.shuffle.memoryFraction
    • 默認值: 0.2
    • 解釋: spark.shuffle.memoryFraction爲true時有效,用於控制shuffle部分
    • 建議: 如果頻繁發生spill(溢寫),可以調大該參數。如果你的應用緩存用的很少,請使勁調大該參數^_^
  • spark.storage.unrollFraction
    • 默認值: 0.2
    • 解釋: spark.memory.useLegacyMode爲true時有效,用於緩存的數據的序列化/反序列化,佔spark.storage.memoryFraction的20%
    • 建議: 可以不調
  • spark.executor.memoryOverhead
    • 默認值: executorMemory * 0.10,最低384M
    • 解釋: 設置每個Executor的堆外內存,主要用於JVM自身,字符串, NIO Buffer等開銷。等於 Container內存 - Executor內存。(沒在官網找到該參數)
    • 建議: Spark使用的是基於nio的,nio使用直接內存區效率非常高,可以適當的分配部分內存到堆外區。
  • spark.locality.wait
    • 默認值: 3s
    • 解釋: 用於指定數據本地化等待時長,包括3個子參數(spark.locality.wait.node、spark.locality.wait.process、spark.locality.wait.rack)用於更細緻的調節。
    • 建議: 通常統一調整spark.locality.wait即可。可以適當加長等待時間,特別是數據量比較大時,如果能夠本地化處理,效果更佳。
  • spark.network.timeout
    • 默認值: 120s
    • 解釋: spark內存通信的網絡延時
    • 建議: 如果spark應用處理比較耗時,那麼可以適當調大該參數(例如300s),防止延時導致的報錯
  • spark.default.parallelism
    • 默認值: 無
    • 解釋: 用於指定RDD的shuffle操作的分區數,例如reduceByKey、join等。調用shuffle算子時,優先使用算子指定的分區數,否則使用spark.default.parallelism的值,如果還是沒值,則使用父RDD的分區數的值。對sql中的shuffle無效。
    • 建議: 如果你想精細化控制,願意爲每個shuffle算子添加parallelism值,那麼不用設置^_^。建議可以先設置一個值(例如總core的2-3倍),然後代碼中有需要調整的就向shuffle算子傳參,覆蓋本次的默認值。(不要誤信網上瞎說的什麼並行度)
  • spark.sql.shuffle.partitions
    • 默認值: 200
    • 解釋: 用於指定SparkSQL執行shuffle時的分區數。(沒在官網找到該參數)
    • 建議: SQL在執行shuffle時,如果默認200小於你的總core數,就會浪費資源了。建議設置爲總core的2-3倍。
  • spark.jars
    • 默認值: 無
    • 解釋: 指定jar包(用逗號分隔),會將其傳到Driver、Executor端
    • 建議: 除了spark lib下的包,其他額外引用的庫建議都指定。(client模式下,只有Driver會用到的庫,可以不傳)
  • spark.jars.excludes
    • 默認值: 無
    • 解釋: 排除不要的包,防止依賴衝突。格式 groupId:artifactId
    • 建議: …
  • spark.executor.userClassPathFirst與spark.driver.userClassPathFirst
    • 默認值: false
    • 解釋: 用於指定是否優先加載用戶指定的jar
    • 建議: 因爲存在用戶指定的jar與spark默認庫下jar包可能衝突的問題,所以當衝突時,如果你能確定你的jar沒問題,那麼可以設置爲true。(之前遇到一個華爲的bug就是這樣)
  • spark.driver.maxResultSize
    • 默認值: 1g
    • 解釋: 數據傳到Driver端的最大量,例如collect、take操作
    • 建議: 有時候可能需要直接拉取大量的數據,可以根據數據量來調整該參數。
  • spark.reducer.maxSizeInFlight
    • 默認值: 48m
    • 解釋: 每個reduce任務拉取數據的最大量
    • 建議: 每個輸出端都需要創建一個buffer,消耗比較大。如果內存比較大,可以提高該值,拉取速度會加快。
  • spark.shuffle.file.buffer
    • 默認值: 32k
    • 解釋: shuffle時,數據輸出到文件的buffer大小,寫滿buffer後,數據會溢寫到磁盤。
    • 建議: 調大該值,可以降低溢寫到磁盤的次數(減少I/O次數),提高性能。內存充足的話,可以調大,例如64k。
  • spark.shuffle.io.maxRetries
    • 默認值: 3
    • 解釋: shuffle時,read端從write端拉取數據的重試次數
    • 建議: 因爲GC、網絡延遲等問題可能會導致拉取失敗,可以適當提高重試次數,防止意外。
  • spark.shuffle.io.retryWait
    • 默認值: 5s
    • 解釋: spark.shuffle.io.maxRetries每次重試需要等多久
    • 建議: 可以加大,提高穩定性
  • spark.shuffle.manager
    • 默認值: sort
    • 解釋: 用於管理shuflle文件輸出到磁盤的方式。建議百度看看詳細流程。1.2版以前默認HashShuffleManager, 之後默認是SortShuffleManager。Spark 2後不再有該參數,直接是SortShuffleManager,默認會對數據進行排序。
    • 建議: 可以通過spark.shuffle.sort.bypassMergeThreshold調整是否排序。
  • spark.shuffle.sort.bypassMergeThreshold
    • 默認值: 200
    • 解釋: shffle時,如果read task數小於200,會啓用bypass機制:不會進行排序操作,最後會合並task產生的文件,並創建索引
    • 建議: 不需要排序時,可以提高該參數,以大於你的shuffle read task數。(會有不錯的效率提升,我的一個應用降低了20%時間)
  • spark.kryo.registrationRequired
    • 默認值: false
    • 解釋: 是否強制kryo序列化
    • 建議: 如果爲false,kyro需要爲每個對象寫未註冊類的類名,會造成顯著的性能開銷。建議設置爲true。(某些朋友對於JVM報出的有的類不知道怎麼註冊,在這裏建議複製報錯的類,使用Class.forName(“類”))
  • spark.rdd.compress
    • 默認值: false
    • 解釋: 是否壓縮序列化的RDD數據
    • 建議: 開啓可以減少內存,但是解壓會增加CPU消耗時間
  • spark.scheduler.mode
    • 默認值: FIFO
    • 解釋: 同一個SparkContext內的調度機制,包括FIFO、FAIR
    • 建議: 一般使用較少。FIFO,先進先出,優先執行先提交的,有空閒的再給後面的job。FAIR,公平分配資源,爲每個可以並行執行的job平均分配計算資源。
  • spark.streaming.backpressure.enabled
    • 默認值: false
    • 解釋: 是否啓用背壓機制,控制SparkStream接收數據的速率。
    • 建議: 流式處理中,處理速度如果較慢,會導致來的數據不斷積壓。啓用後,Spark可以自己動態根據處理能力調整接收數據的量。如果存在積壓情況,建議啓用。另外還有幾個參數,用於細節控制,不建議調整。
  • spark.streaming.blockInterval
    • 默認值: 200ms
    • 解釋: 每批處理的taske數 = 批處理間隔 / blockInterval
    • 建議: blockInterval越大,taske則越少,會導致部分core沒有使用。可以根據你的core的量,適當降低該參數。(官方建議blockInterval最小值約爲50ms)
  • spark.streaming.concurrentJobs
    • 默認值: 1
    • 解釋: 一個SparkStream應用內可以同時運行多少個job。(沒在官網找到該參數)
    • 建議: 如果你分配的core比較多,每批的task數比較少(還可能處理時間比較長),空閒的core比較浪費,那麼可以調高該參數,同時運行後面的job(如果2個job之間沒有前後關聯的話)
  • spark.driver.extraJavaOptions與spark.executor.extraJavaOptions
    • 默認值: 無
    • 解釋: 用於指定JVM參數
    • 建議: 看JVM調參部分

JVM調參

  • 一般不要先調JVM,開發中的大多數問題都是代碼質量不好導致的,先去看代碼、業務邏輯是否有問題,優化、優化、再優化……^_^
  • 然後,確定運行環境,再來調整JVM參數
  • 注意,運行時默認值會隨着系統環境改變,請用 java -XX:+PrintFlagsFinal -version命令查看JVM最終參數
  • 查看GC情況
    • 查看每個節點的GC
      • 添加-XX:PrintGCDetails,讓每個節點打印GC日誌,需要在每個節點分別查看。On YARN的話,可以點擊WebUI界面查看每個節點日誌。
    • 想看Spark應用整體的吞吐量?
      • 每個應用的Spark WebUI有展示,直接在這兒看就可以了。如果GC時間超過10%,那麼說明你的應用需要優化了!
  • 關於GC的選擇(Java 8)
    • 批處理,需要吞吐量較高?用 -XX:+UseParallelGC
    • 流式處理,需要數據具有較高的一致性?用 -XX:+UseConcMarkSweepGC 或 G1
    • 琢磨不定?選 ParallelGC
    • 批處理時想用G1?可以試試,但是吞吐量沒有ParallelGC好(Java 8)
  • 並行垃圾收集器參數(ParallelGC)
    • 以吞吐量優先爲準則
    • -XX:+UseParallelGC 設置年輕代使用ParallelGC,早期版本中老年代會默認使用SerialGC。
    • -XX:+UseParallelOldGC 設置老年代也使用ParallelOldGC,Java1.6後開始支持(我的系統中是默認開啓的,你的不一定)
    • -XX:ParallelGCThreads=8 設置並行收集垃圾的線程數爲8,一般同CPU核個數
    • -XX:MaxGCPauseMillis=100 設置ParallelGC在年輕代單次回收的最長耗時爲100毫秒。如果耗時超過該值,JVM會自動調整年輕代內存大小,以適應該值。可以調大該值,例如500,以保證吞吐量。
    • -XX:GCTimeRatio=99 設置垃圾回收時間佔總時間的百分比最高爲1%,公式爲1/(1+99),即吞吐量爲99%。默認爲99。
    • -XX:+UseAdaptiveSizePolicy 啓用自適應策略。JVM會自動調整年輕代Eden區與Survivor區大小的比例,以適應GCTimeRatio的值。建議開啓。
  • 併發垃圾收集器參數(ConcMarkSweepGC)
    • 以響應時間優先爲準則
    • -XX:+UseConcMarkSweepGC 設置老年代使用ConcMarkSweepGC,年輕代默認使用ParNewGC。
    • -XX:+UseParNewGC 設置年輕代爲ParNewGC。JDK5以上使用CMS時,默認年輕代會採用ParNewGC。
    • -XX:+UseCMSCompactAtFullCollection 開啓壓縮整理(默認),壓縮整理用於消除內存碎片化問題(因爲CMS採用的標記清除算法,不會進行壓縮整理,所以要單獨開啓)
    • -XX:CMSFullGCsBeforeCompaction=2 設置進行2次FullGc後(默認0次),開始對內存進行壓縮整理。
    • -XX:CMSInitiatingOccupancyFraction=70 設置老年代內存使用70%後開始進行併發垃圾收集
  • 內存調整常用參數
    • 常用比例
            young    | old
      eden | s0 | s1 | tenured
      ------------------------
             1       |  2
      8    | 1  | 1  | 
      
    • -Xmx4G 設置JVM最大堆內存爲4G
    • -Xms4G 設置JVM初始堆內存爲4G,一般設爲與-Xmx同樣大即可(避免重新分配內存)
    • -Xmn1G 設置年輕代內存大小爲1G。堆內存 = 老年代 + 年輕代,因此年輕代與老年代之間要根據GC情況取得一個平衡。例如MinorGC較多,可以調大年輕代,MajorGC較多可以調大老年代(即調小年輕代)。
    • -XX:NewSize=512M 設置年輕代初始大小爲512M,一般設爲與MaxNewSize同樣大即可。(不建議使用,直接用-Xmn)
    • -XX:MaxNewSize=1024M 設置年輕代最大大小爲1024M。(不建議使用,直接用-Xmn)
    • -XX:MetaspaceSize=128M 設置元信息區(永久代)初始大小爲256M,一般設爲與MaxMetaspaceSize同樣大即可。(Java8以前叫做-XX:PermSize)
    • -XX:MaxMetaspaceSize=256M 設置元信息區(永久代)最大大小爲256M。(Java8以前叫做-XX:MaxPermSize)
    • -XX:NewRatio=2 設置年輕代與老年代的大小比例爲 1 : 2 (Java8默認爲2)
    • -XX:SurvivorRatio=8 設置年輕代中Eden區與Survivor的大小比例爲 8 : 1 : 1 (enden : survivor0 : survivor1)
    • -Xss256K 設置每個線程私有棧的的大小爲256K(Java8默認爲1M)。線程越多內存佔用越大,需要啓用超多線程時,可以調低該參數,例如128k。
    • -XX:MaxDirectMemory=100M 設置最大堆外內存
  • 其他常用參數
    • -XX:+PrintGCDetails 打印GC詳細信息
    • -XX:+PrintGCTimeStamps 打印GC時的時間戳
    • -Xloggc:/home/jerry/logs/gc.log 設置GC日誌輸出目錄
    • -XX:+HeapDumpOnOutOfMemoryError 當發生內存溢出異常時,dump出堆信息
    • -XX:HeapDumpPath=./my_java.hprof 指定dump堆信息的輸出路徑
    • -XX:MaxTenuringThreshold=8 設置對象在年輕代經歷垃圾回收仍然存活8次後進入老年代。最大值15(因爲JVM用4bit表示該值),默認值15(使用CMS時,默認爲6)
    • -XX:+PrintFlagsFinal 打印JVM最終參數(例如 java -XX:+PrintFlagsFinal -version)
    • -XX:+PrintFlagsInitial 打印JVM初始參數(最終值會隨環境改變)
發佈了143 篇原創文章 · 獲贊 52 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章