Spark 內存管理 原

Spark 內存管理詳解


通常我們討論的是Executor中的內存管理,自1.6版本後spark的內存分爲兩個部分, 堆內內存和堆外內存。下面詳細針對這兩個部分進行詳細的介紹。

堆內內存

堆內內存的大小,由spark應用程序啓動時的--executor-meory或spark.executor.memory參數指定的。 而這一部分內存又分爲3大部分:

  • Reserved Memory: 這一部分使我們無法使用的,spark內部保留的部分,會存儲一些spark的內部對象等內容。默認大小實是300M,無法通過外部參數更改。
  • User Memory: 用戶在程序中創建的對象存儲等一些列非spark管理的內存開銷都佔用這一部分內存。可以這麼認爲,這是程序運行是用戶可以主導的空間,叫做用戶操作空間。默認大小是: (Java Heap - ReservedMemory) * 0.4
  • Spark Memory: 系統框架運行時所需要使用的空間,內部又分爲兩個部分,分別是Storage Memory和Execution Memory。
    • 總默認大小是:(Java Heap - ReservedMemory) * spark.memory.fraction 其中spark.memory.fraction默認大小爲0.6
    • 兩個子部分Storage Memory和Execution Memory,分別各佔0.5,可以使用spark.memory.storageFraction參數來控制storage部分和execution部分的內存佔比。
    • Storage Memory主要用來存儲我們cache的數據和臨時空間序列化時unroll數據
    • Execution Memory則是sparkTask執行時使用的內存(比如shuffle時排序就需要大量的內存)
    • 當然這兩部分的內存劃分也不是十分的嚴格,存在一個動態佔用機制,具體規則見後文

見下圖:

Storage內存和Execution內存之間互相佔用的機制如下:

  • 設定基本的存儲內存和執行內存區域(spark.storage.storageFraction 參數),該設定確定了雙方各自擁有的空間的範圍
  • 雙方的空間都不足時,則存儲到硬盤;若己方空間不足而對方空餘時,可借用對方的空間;(存儲空間不足是指不足以放下一個完整的 Block)
  • 執行內存的空間被對方佔用後,可讓對方將佔用的部分轉存到硬盤,然後"歸還"借用的空間
  • 存儲內存的空間被對方佔用後,無法讓對方"歸還",因爲需要考慮 Shuffle 過程中的很多因素,實現起來較爲複雜

Storage內存管理

當我們需要在計算過程中對一份中間結果使用多次的時候,通常我們需要將中間計算結果緩存下來,spark中提供了cache和persist方法,來實現在內存緩存或磁盤上持久化這個RDD。

RDD的持久化由Spark的Storage模塊負責,實現了RDD與物理存儲的解耦合。Storage模塊負責管理Spark在計算過程中產生的數據,將那些在內存或磁盤、在本地或遠程存取數據的功能封裝了起來。在具體實現時Driver端和Executor端的Storage 模塊構成了主從式的架構,即Driver端的BlockManager爲Master,Executor端的 BlockManager爲Slave。Storage模塊在邏輯上以Block爲基本存儲單位RDD的每個 Partition經過處理後唯一對應一個Block(BlockId的格式爲rdd_RDD-ID_PARTITION-ID)。Master負責整個Spark應用程序的Block的元數據信息的管理和維護,而 Slave需要將Block的更新等狀態上報到Master,同時接收Master的命令,例如新增或刪除一個RDD。 示意圖如下:

RDD在計算過程中,每個task內部計算一個partition,計算過程中產生的Record其實都是存儲在UserMemory中的。

RDD在緩存到內存之後,Partition被轉換成Block,Record在堆內或堆外存儲內存彙總佔用一塊連續的空間。將Partition由不連續的存儲空間轉換爲連續存儲空間的過程,Spark稱之爲"展開"(Unroll)。每個Executor的Storage模塊用一個鏈式 Map 結構(LinkedHashMap)來管理堆內和堆外存儲內存中所有的Block對象的實例,對這個LinkedHashMap 新增和刪除間接記錄了內存的申請和釋放。

執行內存管理

對任務間內存分配

Executor內運行的任務同樣共享執行內存,Spark用一個HashMap結構保存了任務到內存逍遙的映射。每個任務可佔用執行內存大小的範圍爲1/2N~1/N,N爲當前Executor內正在運行的任務個數。每個任務在啓動之時,要向MemoryManager請求申請最少爲1/2N的執行內存,如果不能被滿足要求則該任務被阻塞,直到有其他任務釋放了足夠的執行內存,該任務纔可以被喚醒。

Shuffle內存佔用

執行內存主要用來存儲任務在執行Shuffle時佔用的內存。

  1. 在對reduce端的數據進行聚合時,要將數據交給Aggregator處理,在內存中處理數據是佔用堆內之星空間。
  2. 如果需要進行最終結果排序,則要再次將數據交給ExternalSorter處理,佔用堆內執行空間。

在Aggregator和ExternalSorter中,Spark會使用一種較AppendOnlyMap的哈希表在堆內執行內存中存儲數據,但在Shuffle過程中並不能將所有數據都保存到該哈希表中,當這個哈希表大到一定程度,無法再從MemoryManager中申請到新的執行內存時,Spark就會將其全部內容存儲到磁盤文件中,這個過程被稱爲溢存(Spill),溢存到磁盤的文件最後會被歸併(Merge)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章