必須要懂的Spark記憶體管理模型

  • 2019 年 10 月 31 日
  • 筆記

0、前 言

Apache Spark是目前大數據領域主流的記憶體計算引擎,無論是在批處理還是實時流處理方面都有著廣泛的應用。我們跑作業的時候,首先要給Spark Job分配一定的資源,比如一個executor分配5G記憶體,有時候我們會糾結於executor的記憶體有多少用於了實際計算。因此就需要了解一下Spark的記憶體管理,還有就是掌握了Spark的記憶體模型對於優化我們的作業也至關重要。

在Spark 1.5及之前版本中,記憶體管理默認實現是StaticMemoryManager,稱為靜態記憶體管理。從Spark 1.6.0版本開始,Spark默認採用一種新的記憶體管理模型UnifiedMemoryManager,稱為統一記憶體管理,其特點是可以動態調整Execution和Storage的記憶體,參見SPARK-10000,因此又稱為動態記憶體管理。本文我們就以Executor為例,介紹一下Spark的統一記憶體管理。

1、Spark記憶體管理

首先我們看一下Spark的記憶體使用,主要分為兩類:Execution Memory和Storage Memory。其中,Execution Memory主要用於計算,如shuffles、joins、sorts及aggregations等操作,Storage Memory主要用於cache數據和在集群內部傳輸數據。

Executor默認只使用堆內記憶體(On-heap Memory)。為了進一步優化記憶體的使用,Spark引入了堆外記憶體(Off-heap Memory),默認是關閉狀態。接下來,將詳細說明Spark Executor堆內記憶體與堆外記憶體的具體情況。

1.1 On-heap Memory

Spark Executor通過spark.executor.memory或–executor-memory配置的記憶體為堆內記憶體,可以分為以下四塊區域:

  • Execution Memory:主要用於shuffles、joins、sorts及aggregations等計算操作,又稱為Shuffle Memory。
  • Storage Memory:主要用於cache數據、unroll數據,有時也被稱為Cache Memory。
  • User Memory:用戶記憶體,主要用於存儲內部元數據、用戶自定義的數據結構等,根據用戶實際定義進行使用。
  • Reserved Memory:默認300M的系統預留記憶體,主要用於程式運行,參見SPARK-12081。

各個區域記憶體情況,如下圖所示:

以上圖解中,參數說明如下:

  • 參數spark.memory.fractionSpark 1.6版本中默認0.75,即Spark Memory(Execution Memory + Storage Memory)默認占整個usableMemory(systemMemory – Reserved Memory)記憶體的75%,而在Spark 2.x版本中默認0.6,默認佔usableMemory記憶體的60%。
  • 參數spark.memory.storageFraction默認0.5,代表Storage Memory佔用Spark Memory百分比,(1 – spark.memory.storageFraction)代表Execution Memory佔用Spark Memory百分比,默認值0.5表示Spark Memory中Execution Memory和Storage Memory各佔一半。

1.2 Off-heap Memory

為了進一步優化記憶體的使用,減小GC開銷,Spark 1.6版本還增加了對Off-heap Memory的支援,參見SPARK-11389,但Off-heap Memory默認是關閉的,開啟須設置參數spark.memory.offHeap.enabled為true,並通過參數spark.memory.offHeap.size設置堆外記憶體大小,單位為位元組。

堆外記憶體劃分上沒有了用戶記憶體與預留記憶體,只包含Execution Memory和Storage Memory兩塊區域,記憶體情況如下圖所示:

以上圖解中,maxOffHeapMemory大小就是spark.memory.offHeap.size的值,spark.memory.storageFraction默認值不變。

3、動態記憶體分配

在如上兩個圖解中,我們可以看到Execution Memory與Storage Memory之間有一條可以上下移動的虛線,說明Execution Memory與Storage Memory不是固定不變的,彼此之間可以相互共享,這便是Spark動態記憶體管理的含義。Spark 1.5及之前版本,兩者是固定不變的,即前文提及的靜態記憶體管理。

意思是說,當Execution Memory有空閑,Storage Memory不足時,Storage Memory可以借用Execution Memory,反之亦然。Execution Memory可以讓Storage Memory寫到磁碟,收回被佔用的空間。如果Storage Memory被Execution Memory借用,因為實現上的複雜度,卻收回不了空間。

4、Legacy Mode

Spark 1.6版本開始,默認使用動態(統一)記憶體管理模型,但之前的靜態記憶體管理模型(StaticMemoryManager)仍然保留,通過稱為Legacy模式的參數spark.memory.useLegacyMode控制,默認false為不開啟靜態記憶體管理。

5、總 結

Apache Spark從1.6.0版本開始,其記憶體管理模組默認採用了動態記憶體管理模型,一直延續使用到Spark 2.x。本文參考了社區的一些分享,結合相關圖解,從Spark總體記憶體使用、堆內記憶體、堆外記憶體等幾個方面,重點對Spark的動態記憶體管理這塊做了簡單介紹。