鎖屏面試題百日百刷-Spark篇(六)
鎖屏面試題百日百刷,每個(gè)工作日堅(jiān)持更新面試題。鎖屏面試題app、小程序現(xiàn)已上線,官網(wǎng)地址:https://www.demosoftware.cn。已收錄了每日更新的面試題的所有內(nèi)容,還包含特色的解鎖屏幕復(fù)習(xí)面試題、每日編程題目郵件推送等功能。讓你在面試中先人一步!接下來的是今日的面試題:
1.什么時(shí)候需要啟用checkpoint?
(1)使用了stateful轉(zhuǎn)換-如果?application?中使用了updateStateByKey或reduceByKeyAndWindow等?stateful?操作,必須提供?checkpoint?目錄來允許定時(shí)的RDD checkpoint
(2)希望能從意外中恢復(fù)Driver。
2.導(dǎo)出checkpoint數(shù)據(jù)
<1> checkpoint?的時(shí)機(jī)
在?Spark Streaming?中,JobGenerator?用于生成每個(gè)?batch?對應(yīng)的?jobs,它有一個(gè)定時(shí)器,定時(shí)器的周期即初始化?StreamingContext?時(shí)設(shè)置的?batchDuration。這個(gè)周期一到,JobGenerator?將調(diào)用generateJobs方法來生成并提交?jobs,這之后調(diào)用?doCheckpoint?方法來進(jìn)行?checkpoint。doCheckpoint?方法中,會(huì)判斷當(dāng)前時(shí)間與streaming application start?的時(shí)間之差是否是?checkpoint duration?的倍數(shù),只有在是的情況下才進(jìn)行checkpoint。
<2> checkpoint?的形式
最終?checkpoint?的形式是將類?Checkpoint的實(shí)例序列化后寫入外部存儲(chǔ),值得一提的是,有專門的一條線程來做將序列化后的?checkpoint?寫入外部存儲(chǔ)。
3.Spark的內(nèi)存模型
Spark的Executor的內(nèi)存管理是基于JVM的內(nèi)存管理之上,Spark對JVM堆內(nèi)(On-Heap)空間進(jìn)行了更為詳細(xì)的分配,以便充分利用內(nèi)存,同時(shí)Spark引入堆外內(nèi)存(OffHeap)內(nèi)存,可以直接在Worker節(jié)點(diǎn)的系統(tǒng)內(nèi)存中開辟空間,進(jìn)一步優(yōu)化內(nèi)存使用。
Spark的堆內(nèi)(On-Heap)空間是由--executor-memory或spark.executor.memory參數(shù)配置,Executor內(nèi)運(yùn)行的并發(fā)任務(wù)共享JVM堆內(nèi)內(nèi)存。而且該堆內(nèi)內(nèi)存是一種邏輯上的管理,因?yàn)閷ο蟮尼尫哦际怯蒍VM完成。
Spark引入堆外內(nèi)存(OffHeap)內(nèi)存主要是為了提高Shuffle排序的效率,存儲(chǔ)優(yōu)化過的二進(jìn)制數(shù)據(jù)。從2.0之后Spark可以直接操作系統(tǒng)的堆外內(nèi)存,減少不必要的開銷。改參數(shù)默認(rèn)不開啟,通過spark.memory.offHeap.ennable參數(shù)啟用,并由spark.memory.offHeap.size參數(shù)設(shè)定堆外空間大小。

默認(rèn)情況下,Spark?僅僅使用了堆內(nèi)內(nèi)存。Executor?端的堆內(nèi)內(nèi)存區(qū)域大致可以分為以下四大塊:
Execution?內(nèi)存:主要用于存放?Shuffle、Join、Sort、Aggregation?等計(jì)算過程中的臨時(shí)數(shù)據(jù)
Storage?內(nèi)存:主要用于存儲(chǔ)?spark?的?cache?數(shù)據(jù),例如RDD的緩存、unroll數(shù)據(jù);
用戶內(nèi)存(User Memory):主要用于存儲(chǔ)?RDD?轉(zhuǎn)換操作所需要的數(shù)據(jù),例如?RDD?依賴等信息。
預(yù)留內(nèi)存(Reserved Memory):系統(tǒng)預(yù)留內(nèi)存,會(huì)用來存儲(chǔ)Spark內(nèi)部對象。
?

Execution?內(nèi)存和?Storage?內(nèi)存動(dòng)態(tài)調(diào)整
上面兩張圖中的?Execution?內(nèi)存和?Storage?內(nèi)存之間存在一條虛線,這是為什么呢?
在?Spark 1.5?之前,Execution?內(nèi)存和?Storage?內(nèi)存分配是靜態(tài)的,換句話說就是如果?Execution?內(nèi)存不足,即使?Storage?內(nèi)存有很大空閑程序也是無法利用到的;反之亦然。這就導(dǎo)致我們很難進(jìn)行內(nèi)存的調(diào)優(yōu)工作,我們必須非常清楚地了解?Execution?和?Storage?兩塊區(qū)域的內(nèi)存分布。而目前?Execution?內(nèi)存和?Storage?內(nèi)存可以互相共享的。也就是說,如果?Execution?內(nèi)存不足,而?Storage?內(nèi)存有空閑,那么?Execution?可以從?Storage?中申請空間;反之亦然。所以上圖中的虛線代表?Execution?內(nèi)存和?Storage?內(nèi)存是可以隨著運(yùn)作動(dòng)態(tài)調(diào)整的,這樣可以有效地利用內(nèi)存資源。Execution?內(nèi)存和?Storage?內(nèi)存之間的動(dòng)態(tài)調(diào)整可以概括如下

具體的實(shí)現(xiàn)邏輯如下:
1?程序提交的時(shí)候我們都會(huì)設(shè)定基本的?Execution?內(nèi)存和?Storage?內(nèi)存區(qū)域(通過
spark.memory.storageFraction?參數(shù)設(shè)置);
2?在程序運(yùn)行時(shí),如果雙方的空間都不足時(shí),則存儲(chǔ)到硬盤;將內(nèi)存中的塊存儲(chǔ)到磁盤的策略是按照?LRU規(guī)則進(jìn)行的。若己方空間不足而對方空余時(shí),可借用對方的空間(;?存儲(chǔ)空間不足是指不足以放下一個(gè)完整的?Block);
3 Execution?內(nèi)存的空間被對方占用后,可讓對方將占用的部分轉(zhuǎn)存到硬盤,然后"歸還"借用的空間
4 Storage?內(nèi)存的空間被對方占用后,目前的實(shí)現(xiàn)是無法讓對方"歸還",因?yàn)樾枰紤]?Shuffle過程中的很多因素,實(shí)現(xiàn)起來較為復(fù)雜;而且?Shuffle?過程產(chǎn)生的文件在后面一定會(huì)被使用到,而?Cache在內(nèi)存的數(shù)據(jù)不一定在后面使用。
注意:上面說的借用對方的內(nèi)存需要借用方和被借用方的內(nèi)存類型都一樣,都是堆內(nèi)內(nèi)存或者都是堆外內(nèi)存,不存在堆內(nèi)內(nèi)存不夠去借用堆外內(nèi)存的空間。
Task?之間內(nèi)存分布
為了更好地使用使用內(nèi)存,Executor?內(nèi)運(yùn)行的?Task?之間共享著?Execution?內(nèi)存。具體的,Spark?內(nèi)部維護(hù)了一個(gè)?HashMap?用于記錄每個(gè)?Task?占用的內(nèi)存。當(dāng)?Task?需要在?Execution?內(nèi)存區(qū)域申請?numBytes?內(nèi)存,其先判斷?HashMap?里面是否維護(hù)著這個(gè)?Task?的內(nèi)存使用情況,如果沒有,則將這個(gè)?Task?內(nèi)存使用置為0,并且以?TaskId?為?key,內(nèi)存使用為?value?加入到?HashMap?里面。之后為這個(gè)?Task?申請?numBytes?內(nèi)存,如果Execution?內(nèi)存區(qū)域正好有大于?numBytes?的空閑內(nèi)存,則在?HashMap?里面將當(dāng)前?Task?使用的內(nèi)存加上numBytes,然后返回;如果當(dāng)前?Execution?內(nèi)存區(qū)域無法申請到每個(gè)?Task?最小可申請的內(nèi)存,則當(dāng)前?Task?被阻塞,直到有其他任務(wù)釋放了足夠的執(zhí)行內(nèi)存,該任務(wù)才可以被喚醒。每個(gè)?Task?可以使用?Execution?內(nèi)存大小范圍為?1/2N ~ 1/N,其中?N?為當(dāng)前?Executor?內(nèi)正在運(yùn)行的?Task?個(gè)數(shù)。一個(gè)?Task?能夠運(yùn)行必須申請到最小內(nèi)存為?(1/2N * Execution?內(nèi)存);當(dāng)?N = 1?的時(shí)候,Task?可以使用全部的?Execution?內(nèi)存。
比如如果?Execution?內(nèi)存大小為?10GB,當(dāng)前?Executor?內(nèi)正在運(yùn)行的?Task?個(gè)數(shù)為5,則該?Task?可以申請的內(nèi)存范圍為?10 / (2 * 5) ~ 10 / 5,也就是?1GB ~ 2GB的范圍。