在大規模數據處理中,這個錯誤比較常見。一般發生在有大量shuffle操作的時候,task不斷的failed,然后又重執行,一直循環下去,直到application失敗。
報錯方式
- missing output location
- shuffle fetch faild
SparkSQL shuffle報錯樣例
org.apache.spark.shuffle.MetadataFetchFailedException:
Missing an output location for shuffle 0
org.apache.spark.shuffle.FetchFailedException:
Failed to connect to hostname/192.168.xx.xxx:50268
RDD shuffle報錯樣例
WARN TaskSetManager: Lost task 17.1 in stage 4.1 (TID 1386, spark050013): java.io.FileNotFoundException: /data04/spark/tmp/blockmgr-817d372f-c359-4a00-96dd-8f6554aa19cd/2f/temp_shuffle_e22e013a-5392-4edb-9874-a196a1dad97c (沒有那個文件或目錄)
FetchFailed(BlockManagerId(6083b277-119a-49e8-8a49-3539690a2a3f-S155, spark050013, 8533), shuffleId=1, mapId=143, reduceId=3, message=
org.apache.spark.shuffle.FetchFailedException: Error in opening FileSegmentManagedBuffer{file=/data04/spark/tmp/blockmgr-817d372f-c359-4a00-96dd-8f6554aa19cd/0e/shuffle_1_143_0.data, offset=997061, length=112503}
原因
shuffle分為shuffle write
和shuffle read
兩部分:
- shuffle write:分區數由上一階段的RDD分區數控制
類似于saveAsLocalDiskFile
的操作,將計算的中間結果按某種規則,臨時存放到各個executor所在的本地磁盤上。- shuffle read:分區數由Spark提供的參數控制
如果這個參數值設置的很小,同時shuffle read量很大,那么單個task處理的數據量也會很大,這可能導致JVM crash,從而獲取shuffle數據失敗,同時executor也丟失了,看到Failed to connect to host
的錯誤,也就是executor lost的意思。
有時候即使不會導致JVM crash也會造成長時間的gc。
解決辦法
解決辦法主要從 shuffle的數據量 和 處理shuffle數據的分區數 兩個角度入手。
1. 減少shuffle數據
- 是否可以使用
map side join
或是broadcast join
來規避shuffle。 - 在shuffle前將不必要的數據過濾掉。比如原始數據有20個字段,只要選取需要的字段進行處理即可,將會減少一定的shuffle數據。
2. SparkSQL和DataFrame的join,group by等操作
通過spark.sql.shuffle.partitions
控制分區數,默認為200,根據shuffle的量以及計算的復雜度提高這個值。
3. Rdd的join,groupBy,reduceByKey等操作
通過spark.default.parallelism
控制shuffle read
與reduce
處理的分區數,默認為運行任務的core的總數(mesos細粒度模式為8個,local模式為本地的core總數),官方建議設置成運行任務的core的2-3倍。
4. 提高executor的內存
通過spark.executor.memory
適當提高executor的內存
通過spark.executor.cores
增加每個executor的cpu,這樣不會減少task并行度
5. 是否存在數據傾斜的問題
- 空值是否已經過濾?
- 異常數據(某個key數據特別大)是否可以單獨處理?
- 考慮改變數據分區規則