Spark Heap OOM(堆記憶體溢位)
spark任務在除錯過程中,OOM是非常討厭的一種情況。本文針對Heap OOM的情況先做一定分析,告訴大家如何調參。
1.Heap OOM的現象
如果在Spark UI或者在spark.log中看到如下日誌:
java.lang.OutOfMemoryError: GC overhead limit exceeded
java.lang.OutOfMemoryError: java heap space
或者在gc.log中能看到類似的Full GC日誌:
[Full GC (Ergonomics) [PSYoungGen: 285001K->52041K(465920K)] [ParOldGen: 1398048K->1398049K(1398272K)] 1683050K->1450091K(1864192K), [Metaspace: 35798K->35798K(1081344K)], 3.1598485 secs] [Times: user=12.17 sys=0.09, real=3.16 secs]
此時就很有可能發生了Heap OOM
2.Spark 記憶體模型
Spark框架主要有兩處消耗heap的地方, Spark內部將其分成2個區: Storage和Execution(Execution部分主要用於ShuffleRead和ShuffleWrite).
1.Storage: 主要存RDD, Broadcast等. 涉及的Spark操作: persist/cache/sc.broadcast等
2.Execution: 主要用於Shuffle階段, read shuffle/write shuffle階段需要開buffer來做一些merge操作或者防止shuffle資料放記憶體原地爆炸. 一般涉及的操作: XXXXByKey(reduceByKey,combineByKey等)/coGroup/join類等.
3.Driver heap
Driver heap OOM的三大原因: (1).使用者在Driver埠生成大物件, 比如建立了一個大的集合資料結構 解決思路:
1.1. 考慮將該大物件轉化成Executor端載入. 例如呼叫sc.textFile/sc.hadoopFile等 1.2. 如若無法避免, 自我評估該大物件佔用的記憶體, 相應增加driver-memory的值
(2).從Executor端收集資料回Driver端 比如Collect. 某個Stage中Executor端發回的所有資料量不能超過spark.driver.maxResultSize,預設1g. 如果使用者增加該值, 請對應增加2delta increase到Driver Memory, resultSize該值只是資料序列化之後的Size, 如果是Collect的操作會將這些資料反序列化收集, 此時真正所需記憶體需要膨脹2-5倍, 甚至10倍. *解決思路:
2.1. 本身不建議將大的資料從Executor端, collect回來. 建議將Driver端對collect回來的資料所做的操作, 轉化成Executor端RDD操作. 2.2. 如若無法避免, 自我評collect需要的記憶體, 相應增加driver-memory的值
(3)Spark本身框架的資料消耗. 現在在Spark1.6版本之後主要由Spark UI資料消耗, 取決於作業的累計Task個數.
解決思路: 3.1. 考慮縮小大numPartitions的Stage的partition個數, 例如從HDFS load的partitions一般自動計算, 但是後續使用者的操作中做了過濾等操作已經大大減少資料量, 此時可以縮小Parititions。
3.2. 通過引數spark.ui.retainedStages(預設1000)/spark.ui.retainedJobs(預設1000)控制.
3.3. 實在沒法避免, 相應增加記憶體.
4.Executor heap
UI 表現形式:
UI Task的失敗原因顯示: java.lang.OutOfMemoryError
UI Task的失敗原因顯示: ExecutorLostFailure 和Executor exit code 為143.
UI Task失敗的原因顯示: ExecutorLostFailure 和Executor Lost的原因是Executor heartbeat timed out Spark heap heart beat
OOM原因
(1)資料相關
例如使用者單key對應的Values過多, 呼叫groupByKey/對RDD[K, V: List/Array]做集合操作.
解決思路:
1.1. 控制單key的Value的個數, 做個數截斷或者做過濾. 很多情況是使用者自身有異常資料導致.
1.2. 考慮對業務邏輯的RDD操作, 考慮其他方式的RDD實現, 避免統一處理所有的Values. 比如對Key做且分,類似keyA_1, keyA_2操作.
1.3. 降低spark.memory.fraction的值, 以此提高使用者可用的記憶體空間. 注意spark.memory.fraction的至少保證在0.1. 降低該值會影響Spark的執行效率, 酌情減少。
1.4 增加 Exeutor-memory
(2)使用者程式碼
在RDD操作裡建立了不容易釋放的大物件**, 例如集合操作中產生不易釋放的物件。
解決思路:
2.1. 優化邏輯. 避免在一個RDD操作中實現大量集合操作, 可以嘗試轉化成多個RDD操作.
2.2. 降低spark.memory.fraction的值, 以此提高使用者可用的記憶體空間. 注意spark.memory.fraction的至少保證在0.1, 降低該值會影響Spark的執行效率, 酌情減少。
2.3. 增加Executor-memory.
--------------------- 本文來自 bitcarmanlee 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/bitcarmanlee/article/details/78789679?utm_source=copy