spark常見面試點
一、spark是什么?spark和mapreduce的對比及spark架構(gòu)
spark是什么
spark是一種基于內(nèi)存的大數(shù)據(jù)分析計算引擎。
spark和mapreduce的對比
相同點:spark和mapreduce都是用于大數(shù)據(jù)分析的計算引擎。
不同點(spark比mapreduce快的原因):
1)mapreduce只要簡單的map階段和reduce階段,每一個maptask所產(chǎn)生的的數(shù)據(jù)都會落盤,而且需要經(jīng)過sort combine階段,產(chǎn)生大量磁盤IO,而spark是盡可能高效實用內(nèi)存。
2)對于復(fù)雜計算來說,mapreduce不能進行迭代式計算需要開啟多個應(yīng)用進行處理,也就是需要開啟多個進程處理,而spark可以依賴其dag依賴關(guān)系只需要開啟一個應(yīng)用即可,可以避免重復(fù)啟動應(yīng)用帶來的性能損耗,并且spark的task是線程級別的,基于線程調(diào)度是比進程要快的。
3)從編程模型角度看,spark內(nèi)部根據(jù)不同場景提供了不同類型的高性能算子,并可以將數(shù)據(jù)進行持久化。
綜合以上幾點,所以spark比mapreduce快。
spark架構(gòu)
driver:主要負責(zé)創(chuàng)建SparkContext上下文,提交spark作業(yè),并把?job轉(zhuǎn)換為task,在各個executor進程之間協(xié)調(diào)任務(wù)的調(diào)度。
executor:executor負責(zé)task的執(zhí)行,并把結(jié)果返回給driver。
二、spark編程模型RDD
什么是RDD
RDD是Resilient Distributed Dataset的縮寫。
彈性:存儲彈性,RDD處理的數(shù)據(jù)可以存儲在磁盤也可以存儲在內(nèi)存中 ;計算彈性,計算過程中第一可以根據(jù)需求對數(shù)據(jù)重新分片,第二計算過程中出錯了可以借助緩存自動恢復(fù),第三計算出錯了有重試機制。
RDD的五大特性
getpartitons:Array[Partition] 數(shù)據(jù)的分區(qū)數(shù)(可以設(shè)置分區(qū)數(shù))。
partitioner: 自定義分區(qū)器,控制數(shù)據(jù)進入哪個分區(qū)。
compute:定義分區(qū)數(shù)據(jù)的計算邏輯。
getDenpendencies:RDD的血緣依賴關(guān)系,用于容錯恢復(fù)。
getPreferedLocations:數(shù)據(jù)本地性,移動數(shù)據(jù)不如移動計算,存儲每個partition的計算位置。
? ?
三、spark常用算子
轉(zhuǎn)換算子
1)單value
map,?filter ,mapPartitions,flatMap,glom,coalecse,repartition,groupBy等
2)key value
partitionBy ,groupByKey ,reduceByKey,aggregateBykey,mapValues,join,coGroup等
3)雙 value
交集intersection,并集union,差集subtract:拉鏈zip
行動算子
collect,foreach,reduce,take,first,count,save相關(guān)算子等
其中collect是把收集executor執(zhí)行結(jié)果到driver打印,而foreach則是每個executor分布式調(diào)用foreach打印
四、spark內(nèi)存模型
統(tǒng)一內(nèi)存分布
1)堆內(nèi)內(nèi)存
300M預(yù)留:用于存儲spark的內(nèi)部對象
存儲內(nèi)存:(總spark.executor.memory-300M)*0.6*0.5,用于rdd緩存以及廣播變量
執(zhí)行內(nèi)存:(總spark.executor.memory-300M)*0.6*0.5,用于存儲shuffle過程中的中間數(shù)據(jù)
用戶內(nèi)存:(總spark.executor.memeory-300M)*0.4,用于t存儲ask任務(wù)的執(zhí)行時所需要的數(shù)據(jù)
存儲內(nèi)存+執(zhí)行內(nèi)存((總-300M)*0.6)是有spark.memory.fraction這個參數(shù)控制的,所以如果rdd緩存比較頻繁或者shuffle過程比較多,可以適當調(diào)大spark.memory.fraction,并且可以通過調(diào)節(jié)spark.storage.storagefraction這個參數(shù)來控制存儲內(nèi)存和執(zhí)行內(nèi)存的比例。
2)堆外內(nèi)存
堆外內(nèi)存的調(diào)節(jié)通過spark.executor.memeory.overhead來控制,常見的一個報錯是shuffle file cannot find。這是因為shuffle過程中reduce端從map端拉取數(shù)據(jù)時是從map端的堆外內(nèi)存中拉取數(shù)據(jù)的,大小默認是executor內(nèi)存大小的0.1,最小是384M。內(nèi)存不足時,executor就會掛掉,executor對應(yīng)的BlockManager就掛掉了,reduce拉不到數(shù)據(jù),所以報shuffle file cannot find的問題。一般在提交任務(wù)的時候需要通過spark.executor.memory.overhead這個參數(shù)適當把堆外內(nèi)存調(diào)大(比如2048M)。
并且可以適當調(diào)大spark網(wǎng)絡(luò)連接的超時時長(spark.core.connection.ack.wait.timeout默認120s),因為也有可能是reduce去拉取數(shù)據(jù)的時候,executor上正在執(zhí)行垃圾回收,導(dǎo)致工作線程停止了,所以調(diào)大堆外內(nèi)存的同時適當調(diào)大超時時長也有必要(比如300s)
五、spark調(diào)優(yōu)(資源、shuffle、數(shù)據(jù)傾斜)
1.內(nèi)存
2.shuffle
3.數(shù)據(jù)傾斜
spark中的數(shù)據(jù)傾斜主要分為兩大類
1)reduceByKey和groupByKey這類算子導(dǎo)致的數(shù)據(jù)傾斜問題。一般采用調(diào)整并行度和雙重聚合的方式解決。如果是由于并行度太低導(dǎo)致多個key落到同一個分區(qū)造成的數(shù)據(jù)傾斜問題,可以適當調(diào)大并行度解決。如果是少數(shù)幾個key造成的數(shù)據(jù)傾斜,二次聚合的方式處理,先為每個key增加一個隨機數(shù)前綴進行一次預(yù)聚合,減少數(shù)據(jù)量,然后再把隨機數(shù)前綴去掉,進行二次聚合即可。
2)join過程中產(chǎn)生的數(shù)據(jù)傾斜問題
①數(shù)據(jù)量大的RDDjoin數(shù)據(jù)量小的RDD
這種情況可以將數(shù)據(jù)量小的RDD進行廣播,數(shù)據(jù)量大的RDD通過map類算子結(jié)果廣播變量替換join,避免shuffle過程,也就避免了數(shù)據(jù)傾斜。
②數(shù)據(jù)量大的RDDjoin數(shù)據(jù)量大的RDD
如果造成數(shù)據(jù)傾斜一個RDD中的其中某幾個key造成的
通過分支的思維,通過countByKey算子將產(chǎn)生數(shù)據(jù)傾斜的數(shù)據(jù)和未產(chǎn)生數(shù)據(jù)傾斜的數(shù)據(jù)分別單獨計算,產(chǎn)生數(shù)據(jù)傾斜的那部分數(shù)據(jù)可以采用隨機數(shù)+數(shù)據(jù)膨脹的方式進行處理,最后將結(jié)果union起來即可。
如果造成數(shù)據(jù)傾斜一個RDD中的其中多個key造成的
如果造成數(shù)據(jù)傾斜的可以有很大,那分拆就沒什么效果了。這個時候可以采用隨機數(shù)+擴容的方式進行解決,將其中一個RDD的每個key都打上n以內(nèi)的隨機前綴,另外一個RDD將每條數(shù)據(jù)擴容為n倍,每條數(shù)據(jù)分別依次打上0-n的前綴,這樣也能解決數(shù)據(jù)傾斜問題。
六、spark提交流程原理
以yarn-cluster為例,通過spark-submit提交任務(wù),首先創(chuàng)建yarn-cluster客戶端向ResourceManager提交任務(wù),resourceManger隨之會通知NodeManager啟動該任務(wù)對應(yīng)的ApplicationMaster,ApplicationMaster會啟動Driver線程,Driver向ResourceManager申請相應(yīng)的資源,然后applicationMaster會在合適NodeManager上啟動executor進程,executor進程啟動完畢后向Driver反向注冊。所有executor全部注冊完之后,Driver端開始執(zhí)行main函數(shù),每遇到一個行動算子就會生成一個job,并根據(jù)寬依賴劃分stage,每個stage生成對應(yīng)的TaskSet,之后再將task分發(fā)到executor上執(zhí)行,結(jié)果在反饋給Driver端,任務(wù)結(jié)束后釋放資源。