Spark on Yarn之Executor內存管理

本文1、2、3節介紹了Spark 內存相關之識,第4節描述了常見錯誤類型及產生原因并給出了解決方案。

1 堆內和堆外內存規劃

Executor 的內存管理建立在 JVM 的內存管理之上,Spark 對 JVM 的空間(Heap+Off-heap)進行了更為詳細的分配,以充分利用內存。同時,Spark 引入了Off-heap(TungSten)內存模式,使之可以直接在工作節點的系統內存中開辟空間,進一步優化了內存的使用(可以理解為是獨立于JVM托管的Heap之外利用c-style的malloc從os分配到的memory。由于不再由JVM托管,通過高效的內存管理,可以避免JVM object overhead和Garbage collection的開銷)。

運行于Executor中的Task同時可使用JVM和Off-heap兩種模式的內存。

  • JVM OnHeap內存: 大小由”--executor-memory”(即 spark.executor.memory)參數指定。Executor中運行的并發任務共享JVM堆內內存。
  • JVM OffHeap內存:大小由“spark.yarn.executor.memoryOverhead”參數指定,主要用于JVM自身,字符串, NIO Buffer等開銷。
  • Off-heap模式:默認情況下Off-heap模式的內存并不啟用,可以通過“spark.memory.offHeap.enabled”參數開啟,并由spark.memory.offHeap.size指定堆外內存的大小(占用的空間劃歸JVM OffHeap內存)。

==備注==:我們現在未啟用Off-heap模式的內存,因此,只介紹JVM模式的Executor內存管理。以下出現有Off-heap均為JVM中區別于Heap的內存。

2 Executor內存劃分

2.1 Executor可用內存總量

Executor內存模型

如上圖所示,Yarn集群管理模式中,Spark 以Executor Container的形式在NodeManager中運行,其可使用的內存上限由“yarn.scheduler.maximum-allocation-mb” 指定, ====我們可以稱其為MonitorMemory ====。

如前所述,Executor的內存由Heap內存和設定的Off-heap內存組成。

Heap: 由“spark.executor.memory” 指定, 以下稱為ExecutorMemory
Off-heap: 由 “spark.yarn.executor.memoryOverhead” 指定, 以下稱為MemoryOverhead

因此, 對現有Yarn集群,存在:

ExecutorMemory + MemoryOverhead <= MonitorMemory

若應用提交之時,指定的 ExecutorMemory與MemoryOverhead 之和大于 MonitorMemory,則會導致Executor申請失敗;若運行過程中,實際使用內存超過上限閾值,Executor進程會被Yarn終止掉(kill)。

2.2 Heap

Spark 對Heap內存的管理是邏輯上的劃分管理(限制各有邏輯區域內存量及記錄使用狀態),對象實例真正占用內存的管理(申請和釋放)都由JVM完成。

“spark.executor.memory”指定的內存為JVM最大分配的堆內存(“-xmx”),Spark為了更高效的使用這部分內存,對這部分內存進行了細分,下圖(備注:此圖源于互聯網)對基于spark 2 (1.6)對堆內存分配比例進行了描述:

Heap內存模型

其中:

  1. Reserved Memory保留內存,系統默認值為300,一般無需改動,不用關心此部分內存。 但如果Executor分配的內存小于 1.5 * 300 = 450M時,Executor將無法執行。
  2. Storage Memory 存儲內存
    用于存放廣播數據及RDD緩存數據。由上圖可知,Spark 2+中,初始狀態下,Storage及Execution Memory均約占系統總內存的30%(1 * 0.6 * 0.5 = 0.3)。在UnifiedMemory管理中,這兩部分內存可以相互借用,為了方便描述,我們使用storageRegionSize來表示“spark.storage.storageFraction”。當計算內存不足時,可以改造storageRegionSize中未使用部分,且StorageMemory需要存儲內存時也不可被搶占; 若實際StorageMemory使用量超過storageRegionSize,那么當計算內存不足時,可以改造(StorageMemory – storageRegionSize)部分,而storageRegionSize部分不可被搶占。

==備注==: Unified Memory中,spark.shuffle.memoryFraction, spark.storage.unrollFraction等參數無需在指定。

2.3 Java Off-heap (Memory Overhead)

Executor 中,另一塊內存為由“spark.yarn.executor.memoryOverhead”指定的Java Off-heap內存,此部分內存主要是創建Java Object時的額外開銷,Native方法調用,線程棧, NIO Buffer等開銷(Driect Buffer)。此部分為用戶代碼及Spark 不可操作的內存,不足時可通過調整參數解決, 無需過多關注。 具體需要調整的場景參見本文第4節。

3 任務內存管理(Task Memory Manager)

Executor中任務以線程的方式執行,各線程共享JVM的資源,任務之間的內存資源沒有強隔離(任務沒有專用的Heap區域)。因此,可能會出現這樣的情況:先到達的任務可能占用較大的內存,而后到的任務因得不到足夠的內存而掛起。

在Spark任務內存管理中,使用HashMap存儲任務與其消耗內存的映射關系。每個任務可占用的內存大小為潛在可使用計算內存的1/2n – 1/n , 當剩余內存為小于1/2n時,任務將被掛起,直至有其他任務釋放執行內存,而滿足內存下限1/2n,任務被喚醒,其中n為當前Executor中活躍的任務數。

任務執行過程中,如果需要更多的內存,則會進行申請,如果,存在空閑內存,則自動擴容成功,否則,將拋出OutOffMemroyError。

==備注==:潛在可使用計算內存為:初始計算內存+可搶占存儲內存

4 內存調整方案

Executor中可同時運行的任務數由Executor分配的CPU的核數N 和每個任務需要的CPU核心數C決定。其中:

  • N = spark.executor.cores
  • C = spark.task.cpus

Executor的最大任務并行度可表示為 ==TP = N / C==. 其中,C值與應用類型有關,大部分應用使用默認值1即可,因此,影響Executor中最大任務并行度的主要因素是N.

依據Task的內存使用特征,前文所述的Executor內存模型可以簡單抽象為下圖所示模型:

Executor內存簡化模型

其中,Executor 向yarn申請的總內存可表示為: M = M1 + M2 .

4.1 錯誤類型及調整方案

4.1.1 Executor OOM類錯誤 (錯誤代碼 137、143等)

該類錯誤一般是由于Heap(M2)已達上限,Task需要更多的內存,而又得不到足夠的內存而導致。因此,解決方案要從增加每個Task的內存使用量,滿足任務需求 或 降低單個Task的內存消耗量,從而使現有內存可以滿足任務運行需求兩個角度出發。因此:

4.1.1.1 增加單個task的內存使用量
  • 增加最大Heap值, 即 上圖中M2 的值,使每個Task可使用內存增加。
  • 降低Executor的可用Core的數量 N , 使Executor中同時運行的任務數減少,在總資源不變的情況下,使每個Task獲得的內存相對增加。
4.1.1.2 降低單個Task的內存消耗量

降低單個Task的內存消耗量可從配制方式和調整應用邏輯兩個層面進行優化:

  • 配制方式

    減少每個Task處理的數據量,可降低Task的內存開銷,在Spark中,每個partition對應一個處理任務Task, 因此,在數據總量一定的前提下,可以通過增加partition數量的方式來減少每個Task處理的數據量,從而降低Task的內存開銷。針對不同的Spark應用類型,存在不同的partition調整參數如下:

    • P = spark.default.parallism (非SQL應用)
    • P = spark.sql.shuffle.partition (SQL 應用)

    通過增加P的值,可在一定程度上使Task現有內存滿足任務運行
    注: 當調整一個參數不能解決問題時,上述方案應進行協同調整

    ==備注:若應用shuffle階段 spill嚴重,則可以通過調整“spark.shuffle.spill.numElementsForceSpillThreshold”的值,來限制spill使用的內存大小 ,比如設置(2000000),該值太大不足以解決OOM問題,若太小,則spill會太頻繁,影響集群性能,因此,要依據負載類型進行合理伸縮(此處,可設法引入動態伸縮機制,待后續處理)。==

  • 調整應用邏輯

    Executor OOM 一般發生Shuffle階段,該階段需求計算內存較大,且應用邏輯對內存需求有較大影響,下面舉例就行說明:

    • groupByKey 轉換為 reduceByKey
      一般情況下,groupByKey能實現的功能使用reduceByKey均可實現,而ReduceByKey存在Map端的合并,可以有效減少傳輸帶寬占用及Reduce端內存消耗。
選擇合適的算子
  • data skew 預處理

    Data Skew是指任務間處理的數據量存大較大的差異。
    如左圖所示,key 為010的數據較多,當發生shuffle時,010所在分區存在大量數據,不僅拖慢Job執行(Job的執行時間由最后完成的任務決定)。 而且導致010對應Task內存消耗過多,可能導致OOM. 而右圖,經過預處理(加鹽,此處僅為舉例說明問題,解決方法不限于此)可以有效減少Data Skew導致 的問題

Data Skew預處理
==注:上述舉例僅為說明調整應用邏輯可以在一定程序上解決OOM問題,解決方法不限于上述舉例==

4.1.2 Beyond…… memory, killed by yarn.

出現該問題原因是由于實際使用內存上限超過申請的內存上限而被Yarn終止掉了, 首先說明Yarn中Container內存監控機制:

  • Container進程的內存使用量:以Container進程為根的進程樹中所有進程的內存使用總量。
  • Container被殺死的判斷依據:進程樹總內存(物理內存或虛擬內存)使用量超過向Yarn申請的內存上限值,則認為該Container使用內存超量,可以被“殺死”。

因此,對該異常的分析要從是否存在子進程兩個角度出發。

1) 不存在子進程

根據Container進程殺死的條件可知,在不存在子進程時,出現killed by yarn問題是于由Executor(JVM)進程自身內存超過向Yarn申請的內存總量M 所致。由于未出現4.1.1節所述的OOM異常,因此可判定其為 M1 (Overhead)不足, 依據Yarn內存使用情況有如下兩種方案:

  • 如果,M未達到Yarn單個Container允許的上限時,可僅增加M1 ,從而增加M;如果,M達到Yarn單個Container允許的上限時,增加 M1, 降低 M2.
    操作方法:在提交腳本中添加 --conf spark.yarn.executor.memoryOverhead=3072(或更大的值,比如4096等) --conf spark.executor.memory = 10g 或 更小的值,注意二者之各要小于Container監控內存量,否則伸請資源將被yarn拒絕。
  • 減少可用的Core的數量 N, 使并行任務數減少,從而減少Overhead開銷
    操作方法:在提交腳本中添加 --executor-cores=3 <比原來小的值> 或 --conf spark.executor.cores=3 <比原來小的值>

2)存在子進程
Spark 應用中Container以Executor(JVM進程)的形式存在,因此根進程為Executor對應的進程, 而Spark 應用向Yarn申請的總資源M = M1 + M 2 , 都是以Executor(JVM)進程(非進程樹)可用資源的名義申請的。申請的資源并非一次性全量分配給JVM使用,而是先為JVM分配初始值,隨后內存不足時再按比率不斷進行擴容,直致達到Container監控的最大內存使用量M 。當Executor中啟動了子進程(調用shell等)時,子進程占用的內存(記為 S) 就被加入Container進程樹,此時就會影響Executor實際可使用內存資源(Executor進程實際可使用資源為:M - S),然而啟動JVM時設置的可用最大資源為M, 且JVM進程并不會感知Container中留給自己的使用量已被子進程占用,因此,當JVM使用量達到 M - S,還會繼續開劈內存空間,這就會導致Executor進程樹使用的總內存量大于M 而被Yarn 殺死。

典形場景有:PySpark(Spark已做內存限制,一般不會占用過大內存)、自定義Shell調用。其解決方案:

  • PySpark場景:
    • 如果,M未達到Yarn單個Container允許的上限時,可僅增加M1 ,從而增加M;如果,M達到Yarn單個Container允許的上限時,增加 M1, 降低 M2.
    • 減少可用的Core的數量 N, 使并行任務數減少,從而減少Overhead開銷
  • 自定義Shell 場景:(OverHead不足為假象)
    • 調整子進程可用內存量,(通過單機測試,內存控制在Container監控內存以內,且為Spark保留內存等留有空間)。
      操作方法同4.1.2<1>中所述

特別鳴謝:海華師兄
轉載請注明:http://www.lxweimin.com/p/10e91ace3378

參考文獻:
https://spark.apache.org/docs/latest/tuning.html
https://0x0fff.com/spark-memory-management/ https://www.ibm.com/developerworks/cn/analytics/library/ba-cn-apache-spark-memory-management/index.html?ca=drs-&utm_source=tuicool&utm_medium=referral

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內容

  • YarnYarn產生背景:Yarn直接來自于MR1.0MR1.0 問題:采用的是master slave結構,ma...
    時待吾閱讀 5,781評論 2 23
  • Apache Spark是一個圍繞速度、易用性和復雜分析構建的大數據處理框架,最初在2009年由加州大學伯克利分校...
    三萬_chenbing閱讀 641評論 0 5
  • 原文地址: http://blog.javachen.com/2015/06/09/memory-in-Spark...
    木頭人_9307閱讀 2,718評論 0 4
  • 偶然了解Stetho,嘗試加入了Widgets套餐 引入步驟 模塊gradle中引入依賴: 如果需要監測網絡數據,...
    cherishyan閱讀 697評論 0 0
  • 這里的路燈有很多種樣子 帶著每一種可能的遇見 煤油燈的窗口旁 一塊高地的燭光 我該忘了哪一樹下的你 蒙蒙細絲縷灑下...
    咸忻徽閱讀 185評論 0 1