Spark作為一個(gè)基于內(nèi)存的分布式計(jì)算引擎,其內(nèi)存管理模塊在整個(gè)系統(tǒng)中扮演著非常重要的角色帮匾。理解Spark內(nèi)存管理的基本原理,有助于更好地開(kāi)發(fā)Spark應(yīng)用程序和進(jìn)行性能調(diào)優(yōu)痴鳄。
我們都知道瘟斜,在執(zhí)行Spark的應(yīng)用程序時(shí),Spark集群會(huì)啟動(dòng)Driver和Executor兩種JVM進(jìn)程痪寻,作用分別如下螺句,
Driver:創(chuàng)建SparkContent;劃分Job Stage以及轉(zhuǎn)化Task橡类;Executor進(jìn)程間協(xié)調(diào)任務(wù)的調(diào)度蛇尚;
Executor:執(zhí)行具體的計(jì)算任務(wù),將結(jié)果返回給Driver猫态;持久化RDD
Driver的內(nèi)存管理相對(duì)來(lái)說(shuō)較為簡(jiǎn)單佣蓉,Spark不做具體規(guī)劃披摄。
作為一個(gè)JVM進(jìn)程,Executor的內(nèi)存管理建立在JVM的內(nèi)存管理之上勇凭,Spark對(duì)JVM的堆內(nèi)(On-heap)空間進(jìn)行了更為詳細(xì)的分配疚膊,以充分利用內(nèi)存。同時(shí)虾标,Spark引入了堆外(Off-heap)內(nèi)存寓盗,使之可以直接在工作節(jié)點(diǎn)的系統(tǒng)內(nèi)存中開(kāi)辟空間,進(jìn)一步優(yōu)化了內(nèi)存的使用璧函。
- 堆內(nèi)內(nèi)存:受JVM管理
- 堆外內(nèi)存:不受jvm管理
1傀蚌、堆內(nèi)內(nèi)存 on-heap Memory
堆內(nèi)內(nèi)存的大小,由Spark應(yīng)用程序啟動(dòng)時(shí)的–executor-memory或spark.executor.memory參數(shù)配置蘸吓。
Executor內(nèi)運(yùn)行的并發(fā)任務(wù)共享JVM堆內(nèi)內(nèi)存善炫。
Executor內(nèi)存空間的分配:
- 存儲(chǔ)Storage內(nèi)存區(qū)域:緩存RDD和廣播(Broadcast)數(shù)據(jù);
- 執(zhí)行Execution內(nèi)存區(qū)域:執(zhí)行Shuffle時(shí)占用的內(nèi)存库继;
- 其他:Spark內(nèi)部元數(shù)據(jù)箩艺,或者用戶定義的數(shù)據(jù)結(jié)構(gòu)
Spark為存儲(chǔ)內(nèi)存和執(zhí)行內(nèi)存的管理提供了統(tǒng)一的接口——MemoryManager,同一個(gè) Executor內(nèi)的任務(wù)都調(diào)用這個(gè)接口的方法來(lái)申請(qǐng)或釋放內(nèi)存宪萄。
MemoryManager有兩種具體實(shí)現(xiàn)艺谆,Spark1.6 之后默認(rèn)為統(tǒng)一管理(UnifiedMemoryManager)方式,1.6 之前采用的靜態(tài)管理(StaticMemoryManager)方式仍被保留拜英,可通過(guò)配置spark.memory.useLegacyMode參數(shù)啟用静汤。
兩種方式的區(qū)別在于對(duì)空間分配的方式。
1.1 StaticMemoryManager 靜態(tài)管理
存儲(chǔ)內(nèi)存居凶、執(zhí)行內(nèi)存和其他內(nèi)存的大小在Spark應(yīng)用程序運(yùn)行期間均為固定的虫给,但用戶可以應(yīng)用程序啟動(dòng)前進(jìn)行配置;
存儲(chǔ)內(nèi)存默認(rèn)占用堆內(nèi)內(nèi)存的60%排监;
執(zhí)行內(nèi)存默認(rèn)占用堆內(nèi)內(nèi)存的20%狰右;——shuffle調(diào)優(yōu)時(shí)杰捂,可以調(diào)整這個(gè)比例舆床,從而給shuffle過(guò)程更多的內(nèi)存
其他內(nèi)存默認(rèn)占用堆內(nèi)內(nèi)存的20%;
這種方式有個(gè)缺點(diǎn)嫁佳,那就是如果用戶不熟悉Spark的存儲(chǔ)機(jī)制挨队,或沒(méi)有根據(jù)具體的數(shù)據(jù)規(guī)模和計(jì)算任務(wù)或做相應(yīng)的配置,容易出現(xiàn)存儲(chǔ)內(nèi)存和執(zhí)行內(nèi)存中的一方剩余大量的空間蒿往,而另一方卻早早被占滿盛垦,不得不淘汰或移出舊的內(nèi)容以存儲(chǔ)新的內(nèi)容,造成程序執(zhí)行緩慢甚至失敗
1.2 UnifiedMemoryManager 統(tǒng)一管理
Spark1.6 之后引入的統(tǒng)一內(nèi)存管理機(jī)制瓤漏,與靜態(tài)內(nèi)存管理的區(qū)別在于存儲(chǔ)內(nèi)存和執(zhí)行內(nèi)存共享同一塊空間腾夯,可以動(dòng)態(tài)占用對(duì)方的空閑區(qū)域颊埃。
存儲(chǔ)內(nèi)存+執(zhí)行內(nèi)存=統(tǒng)一內(nèi)存 默認(rèn)占用堆內(nèi)內(nèi)存的60%;
存儲(chǔ)內(nèi)存和執(zhí)行內(nèi)存 默認(rèn)各占用統(tǒng)一內(nèi)存50%蝶俱;——可用spark.storage.storageFraction參數(shù)調(diào)整
其他內(nèi)存 默認(rèn)占用堆內(nèi)內(nèi)存的40%班利;
優(yōu)點(diǎn):在一定程度上提高了堆內(nèi)和堆外內(nèi)存資源的利用率,降低了開(kāi)發(fā)者維護(hù)Spark內(nèi)存的難度
動(dòng)態(tài)占用機(jī)制的規(guī)則如下:
- 1.設(shè)定基本的存儲(chǔ)內(nèi)存和執(zhí)行內(nèi)存區(qū)域(spark.storage.storageFraction參數(shù))榨呆,該設(shè)定確定了雙方各自擁有的空間的范圍
- 2.雙方的空間都不足時(shí)罗标,則存儲(chǔ)到硬盤(pán);若己方空間不足而對(duì)方空余時(shí)积蜻,可借用對(duì)方的空間;(存儲(chǔ)空間不足是指不足以放下一個(gè)完整的Block)
- 3.執(zhí)行內(nèi)存的空間被對(duì)方占用后闯割,可讓對(duì)方將占用的部分轉(zhuǎn)存到硬盤(pán),然后“歸還”借用的空間
- 4.存儲(chǔ)內(nèi)存的空間被對(duì)方占用后竿拆,無(wú)法讓對(duì)方“歸還”宙拉,因?yàn)樾枰紤]Shuffle過(guò)程中的很多因素,實(shí)現(xiàn)起來(lái)較為復(fù)雜
2丙笋、堆外內(nèi)存
在默認(rèn)情況下鼓黔,堆外內(nèi)存并不啟用,可通過(guò)配置spark.memory.offHeap.enabled參數(shù)啟用不见,并由spark.memory.offHeap.size參數(shù)設(shè)定堆外空間的大小澳化。
堆外內(nèi)存主要存儲(chǔ)經(jīng)過(guò)序列化的二進(jìn)制數(shù)據(jù)。
Spark可以直接操作系統(tǒng)堆外內(nèi)存稳吮,減少了不必要的內(nèi)存開(kāi)銷(xiāo)缎谷,以及頻繁的GC掃描和回收,提升了處理性能灶似。堆外內(nèi)存可以被精確地申請(qǐng)和釋放列林,而且序列化的數(shù)據(jù)占用的空間可以被精確計(jì)算,所以相比堆內(nèi)內(nèi)存來(lái)說(shuō)降低了管理的難度酪惭,也降低了誤差希痴。
堆外的空間分配較為簡(jiǎn)單,除了沒(méi)有 other空間春感,存儲(chǔ)內(nèi)存砌创、執(zhí)行內(nèi)存的大小同樣是固定的,所有運(yùn)行中的并發(fā)任務(wù)共享存儲(chǔ)內(nèi)存和執(zhí)行內(nèi)存鲫懒。
2.1 StaticMemoryManager 靜態(tài)管理
堆外的空間分配較為簡(jiǎn)單嫩实,存儲(chǔ)內(nèi)存和執(zhí)行內(nèi)存的大小同樣是固定的,可用的執(zhí)行內(nèi)存和存儲(chǔ)內(nèi)存占用的空間大小直接由參數(shù)spark.memory.storageFraction決定窥岩,由于堆外內(nèi)存占用的空間可以被精確計(jì)算甲献,所以無(wú)需再設(shè)定保險(xiǎn)區(qū)域。
2.2 UnifiedMemoryManager 統(tǒng)一管理
3颂翼、總結(jié)
憑借統(tǒng)一內(nèi)存管理機(jī)制晃洒,Spark在一定程度上提高了堆內(nèi)和堆外內(nèi)存資源的利用率慨灭,降低了開(kāi)發(fā)者維護(hù)Spark內(nèi)存的難度,但并不意味著開(kāi)發(fā)者可以高枕無(wú)憂球及。譬如缘挑,所以如果存儲(chǔ)內(nèi)存的空間太大或者說(shuō)緩存的數(shù)據(jù)過(guò)多,反而會(huì)導(dǎo)致頻繁的全量垃圾回收桶略,降低任務(wù)執(zhí)行時(shí)的性能语淘,因?yàn)榫彺娴腞DD數(shù)據(jù)通常都是長(zhǎng)期駐留內(nèi)存的[5]。所以要想充分發(fā)揮Spark的性能际歼,需要開(kāi)發(fā)者進(jìn)一步了解存儲(chǔ)內(nèi)存和執(zhí)行內(nèi)存各自的管理方式和實(shí)現(xiàn)原理惶翻。
如果是你的計(jì)算比較復(fù)雜的情況,使用新型的統(tǒng)一內(nèi)存管理 (Unified Memory Management) 會(huì)取得更好的效率鹅心,但是如果說(shuō)計(jì)算的業(yè)務(wù)邏輯需要更大的緩存空間吕粗,此時(shí)使用老版本的固定內(nèi)存管理 (StaticMemoryManagement) 效果會(huì)更好。
參考:https://blog.csdn.net/dongdouzin/article/details/79753155