[圖片上傳失敗...(image-519c60-1588930981250)]
[圖片上傳失敗...(image-529391-1588930981250)]
[圖片上傳失敗...(image-8667f7-1588930981250)]
[圖片上傳失敗...(image-c79b1c-1588930981250)]
[圖片上傳失敗...(image-a41e47-1588930981250)]
1.spark中的RDD是什么,有哪些特性?
答:RDD(Resilient Distributed Dataset)叫做分布式數據集,是spark中最基本的數據抽象,它代表一個不可變,可分區,里面的元素可以并行計算的集合
Dataset:就是一個集合,用于存放數據的
Destributed:分布式,可以并行在集群計算
Resilient:表示彈性的,彈性表示
1.RDD中的數據可以存儲在內存或者磁盤中;
2.RDD中的分區是可以改變的;
五大特性:
1.A list of partitions:一個分區列表,RDD中的數據都存儲在一個分區列表中
2.A function for computing each split:作用在每一個分區中的函數
3.A list of dependencies on other RDDs:一個RDD依賴于其他多個RDD,這個點很重要,RDD的容錯機制就是依據這個特性而來的
4.Optionally,a Partitioner for key-value RDDs(eg:to say that the RDD is hash-partitioned):可選的,針對于kv類型的RDD才有這個特性,作用是決定了數據的來源以及數據處理后的去向
5.可選項,數據本地性,數據位置最優
2.概述一下spark中的常用算子區別(map,mapPartitions,foreach,foreachPatition)
答:map:用于遍歷RDD,將函數應用于每一個元素,返回新的RDD(transformation算子)
foreach:用于遍歷RDD,將函數應用于每一個元素,無返回值(action算子)
mapPatitions:用于遍歷操作RDD中的每一個分區,返回生成一個新的RDD(transformation算子)
foreachPatition:用于遍歷操作RDD中的每一個分區,無返回值(action算子)
總結:一般使用mapPatitions和foreachPatition算子比map和foreach更加高效,推薦使用
3.談談spark中的寬窄依賴:
答:RDD和它的父RDD的關系有兩種類型:窄依賴和寬依賴
寬依賴:指的是多個子RDD的Partition會依賴同一個父RDD的Partition,關系是一對多,父RDD的一個分區的數據去到子RDD的不同分區里面,會有shuffle的產生
窄依賴:指的是每一個父RDD的Partition最多被子RDD的一個partition使用,是一對一的,也就是父RDD的一個分區去到了子RDD的一個分區中,這個過程沒有shuffle產生
區分的標準就是看父RDD的一個分區的數據的流向,要是流向一個partition的話就是窄依賴,否則就是寬依賴,如圖所示:
4.spark中如何劃分stage:
答:概念:Spark任務會根據RDD之間的依賴關系,形成一個DAG有向無環圖,DAG會提交給DAGScheduler,DAGScheduler會把DAG劃分相互依賴的多個stage,劃分依據就是寬窄依賴,遇到寬依賴就劃分stage,每個stage包含一個或多個task,然后將這些task以taskSet的形式提交給TaskScheduler運行,stage是由一組并行的task組成
1.spark程序中可以因為不同的action觸發眾多的job,一個程序中可以有很多的job,每一個job是由一個或者多個stage構成的,后面的stage依賴于前面的stage,也就是說只有前面依賴的stage計算完畢后,后面的stage才會運行;
2.stage 的劃分標準就是寬依賴:何時產生寬依賴就會產生一個新的stage,例如reduceByKey,groupByKey,join的算子,會導致寬依賴的產生;
3.切割規則:從后往前,遇到寬依賴就切割stage;
4.圖解:
5.計算格式:pipeline管道計算模式,piepeline只是一種計算思想,一種模式
6.spark的pipeline管道計算模式相當于執行了一個高階函數,也就是說來一條數據然后計算一條數據,會把所有的邏輯走完,然后落地,而MapReduce是1+1=2,2+1=3這樣的計算模式,也就是計算完落地,然后再計算,然后再落地到磁盤或者內存,最后數據是落在計算節點上,按reduce的hash分區落地。管道計算模式完全基于內存計算,所以比MapReduce快的原因。
7.管道中的RDD何時落地:shuffle write的時候,對RDD進行持久化的時候。
8.stage的task的并行度是由stage的最后一個RDD的分區數來決定的,一般來說,一個partition對應一個task,但最后reduce的時候可以手動改變reduce的個數,也就是改變最后一個RDD的分區數,也就改變了并行度。例如:reduceByKey(+,3)
9.優化:提高stage的并行度:reduceByKey(+,patition的個數) ,join(+,patition的個數)
4.DAGScheduler分析:
答:概述:是一個面向stage 的調度器;
主要入參有:dagScheduler.runJob(rdd, cleanedFunc, partitions, callSite, allowLocal,resultHandler, localProperties.get)
rdd: final RDD;
cleanedFunc: 計算每個分區的函數;
resultHander: 結果偵聽器;
主要功能:1.接受用戶提交的job;
2.將job根據類型劃分為不同的stage,記錄那些RDD,stage被物化,并在每一個stage內產生一系列的task,并封裝成taskset;
3.決定每個task的最佳位置,任務在數據所在節點上運行,并結合當前的緩存情況,將taskSet提交給TaskScheduler;
4.重新提交shuffle輸出丟失的stage給taskScheduler;
注:一個stage內部的錯誤不是由shuffle輸出丟失造成的,DAGScheduler是不管的,由TaskScheduler負責嘗試重新提交task執行。
5.Job的生成:
答:一旦driver程序中出現action,就會生成一個job,比如count等,向DAGScheduler提交job,如果driver程序后面還有action,那么其他action也會對應生成相應的job,所以,driver端有多少action就會提交多少job,這可能就是為什么spark將driver程序稱為application而不是job 的原因。每一個job可能會包含一個或者多個stage,最后一個stage生成result,在提交job 的過程中,DAGScheduler會首先從后往前劃分stage,劃分的標準就是寬依賴,一旦遇到寬依賴就劃分,然后先提交沒有父階段的stage們,并在提交過程中,計算該stage的task數目以及類型,并提交具體的task,在這些無父階段的stage提交完之后,依賴該stage 的stage才會提交。
6.有向無環圖:
答:DAG,有向無環圖,簡單的來說,就是一個由頂點和有方向性的邊構成的圖中,從任意一個頂點出發,沒有任意一條路徑會將其帶回到出發點的頂點位置,為每個spark job計算具有依賴關系的多個stage任務階段,通常根據shuffle來劃分stage,如reduceByKey,groupByKey等涉及到shuffle的transformation就會產生新的stage ,然后將每個stage劃分為具體的一組任務,以TaskSets的形式提交給底層的任務調度模塊來執行,其中不同stage之前的RDD為寬依賴關系,TaskScheduler任務調度模塊負責具體啟動任務,監控和匯報任務運行情況。
7.RDD是什么以及它的分類:
8.RDD的操作
9.RDD緩存:
Spark可以使用 persist 和 cache 方法將任意 RDD 緩存到內存、磁盤文件系統中。緩存是容錯的,如果一個 RDD 分片丟失,可以通過構建它的 transformation自動重構。被緩存的 RDD 被使用的時,存取速度會被大大加速。一般的executor內存60%做 cache, 剩下的40%做task。
Spark中,RDD類可以使用cache() 和 persist() 方法來緩存。cache()是persist()的特例,將該RDD緩存到內存中。而persist可以指定一個StorageLevel。StorageLevel的列表可以在StorageLevel 伴生單例對象中找到。
Spark的不同StorageLevel ,目的滿足內存使用和CPU效率權衡上的不同需求。我們建議通過以下的步驟來進行選擇:
·如果你的RDDs可以很好的與默認的存儲級別(MEMORY_ONLY)契合,就不需要做任何修改了。這已經是CPU使用效率最高的選項,它使得RDDs的操作盡可能的快。
·如果不行,試著使用MEMORY_ONLY_SER并且選擇一個快速序列化的庫使得對象在有比較高的空間使用率的情況下,依然可以較快被訪問。
·盡可能不要存儲到硬盤上,除非計算數據集的函數,計算量特別大,或者它們過濾了大量的數據。否則,重新計算一個分區的速度,和與從硬盤中讀取基本差不多快。
·如果你想有快速故障恢復能力,使用復制存儲級別(例如:用Spark來響應web應用的請求)。所有的存儲級別都有通過重新計算丟失數據恢復錯誤的容錯機制,但是復制存儲級別可以讓你在RDD上持續的運行任務,而不需要等待丟失的分區被重新計算。
·如果你想要定義你自己的存儲級別(比如復制因子為3而不是2),可以使用StorageLevel 單例對象的apply()方法。
在不會使用cached RDD的時候,及時使用unpersist方法來釋放它。
10.RDD共享變量:
在應用開發中,一個函數被傳遞給Spark操作(例如map和reduce),在一個遠程集群上運行,它實際上操作的是這個函數用到的所有變量的獨立拷貝。這些變量會被拷貝到每一臺機器。通常看來,在任務之間中,讀寫共享變量顯然不夠高效。然而,Spark還是為兩種常見的使用模式,提供了兩種有限的共享變量:廣播變量和累加器。
(1). 廣播變量(Broadcast Variables)
– 廣播變量緩存到各個節點的內存中,而不是每個 Task
– 廣播變量被創建后,能在集群中運行的任何函數調用
– 廣播變量是只讀的,不能在被廣播后修改
– 對于大數據集的廣播, Spark 嘗試使用高效的廣播算法來降低通信成本
val broadcastVar = sc.broadcast(Array(1, 2, 3))方法參數中是要廣播的變量
(2). 累加器
累加器只支持加法操作,可以高效地并行,用于實現計數器和變量求和。Spark 原生支持數值類型和標準可變集合的計數器,但用戶可以添加新的類型。只有驅動程序才能獲取累加器的值
11.spark-submit的時候如何引入外部jar包:
在通過spark-submit提交任務時,可以通過添加配置參數來指定
–driver-class-path 外部jar包
–jars 外部jar包
12.spark如何防止內存溢出:
driver端的內存溢出
可以增大driver的內存參數:spark.driver.memory (default 1g)
這個參數用來設置Driver的內存。在Spark程序中,SparkContext,DAGScheduler都是運行在Driver端的。對應rdd的Stage切分也是在Driver端運行,如果用戶自己寫的程序有過多的步驟,切分出過多的Stage,這部分信息消耗的是Driver的內存,這個時候就需要調大Driver的內存。
map過程產生大量對象導致內存溢出
這種溢出的原因是在單個map中產生了大量的對象導致的,例如:rdd.map(x=>for(i <- 1 to 10000) yield i.toString),這個操作在rdd中,每個對象都產生了10000個對象,這肯定很容易產生內存溢出的問題。針對這種問題,在不增加內存的情況下,可以通過減少每個Task的大小,以便達到每個Task即使產生大量的對象Executor的內存也能夠裝得下。具體做法可以在會產生大量對象的map操作之前調用repartition方法,分區成更小的塊傳入map。例如:rdd.repartition(10000).map(x=>for(i <- 1 to 10000) yield i.toString)。
面對這種問題注意,不能使用rdd.coalesce方法,這個方法只能減少分區,不能增加分區, 不會有shuffle的過程。
數據不平衡導致內存溢出
數據不平衡除了有可能導致內存溢出外,也有可能導致性能的問題,解決方法和上面說的類似,就是調用repartition重新分區。這里就不再累贅了。
shuffle后內存溢出
shuffle內存溢出的情況可以說都是shuffle后,單個文件過大導致的。在Spark中,join,reduceByKey這一類型的過程,都會有shuffle的過程,在shuffle的使用,需要傳入一個partitioner,大部分Spark中的shuffle操作,默認的partitioner都是HashPatitioner,默認值是父RDD中最大的分區數,這個參數通過spark.default.parallelism控制(在spark-sql中用spark.sql.shuffle.partitions) , spark.default.parallelism參數只對HashPartitioner有效,所以如果是別的Partitioner或者自己實現的Partitioner就不能使用spark.default.parallelism這個參數來控制shuffle的并發量了。如果是別的partitioner導致的shuffle內存溢出,就需要從partitioner的代碼增加partitions的數量。
standalone模式下資源分配不均勻導致內存溢出
在standalone的模式下如果配置了–total-executor-cores 和 –executor-memory 這兩個參數,但是沒有配置–executor-cores這個參數的話,就有可能導致,每個Executor的memory是一樣的,但是cores的數量不同,那么在cores數量多的Executor中,由于能夠同時執行多個Task,就容易導致內存溢出的情況。這種情況的解決方法就是同時配置–executor-cores或者spark.executor.cores參數,確保Executor資源分配均勻。
使用rdd.persist(StorageLevel.MEMORY_AND_DISK_SER)代替rdd.cache()
rdd.cache()和rdd.persist(Storage.MEMORY_ONLY)是等價的,在內存不足的時候rdd.cache()的數據會丟失,再次使用的時候會重算,而rdd.persist(StorageLevel.MEMORY_AND_DISK_SER)在內存不足的時候會存儲在磁盤,避免重算,只是消耗點IO時間。
13.spark中cache和persist的區別:
cache:緩存數據,默認是緩存在內存中,其本質還是調用persist
persist:緩存數據,有豐富的數據緩存策略。數據可以保存在內存也可以保存在磁盤中,使用的時候指定對應的緩存級別就可以了。
14.spark分布式集群搭建的步驟:
地球人都知道
這里可以概述下如何搭建高可用的spark集群(HA)
主要是引入了zookeeper
15.spark中的數據傾斜的現象,原因,后果:
(1)、數據傾斜的現象
多數task執行速度較快,少數task執行時間非常長,或者等待很長時間后提示你內存不足,執行失敗。
(2)、數據傾斜的原因
數據問題
1、key本身分布不均衡(包括大量的key為空)
2、key的設置不合理
spark使用問題
1、shuffle時的并發度不夠
2、計算方式有誤
(3)、數據傾斜的后果
1、spark中的stage的執行時間受限于最后那個執行完成的task,因此運行緩慢的任務會拖垮整個程序的運行速度(分布式程序運行的速度是由最慢的那個task決定的)。
2、過多的數據在同一個task中運行,將會把executor撐爆。
16.spark數據傾斜的處理:
發現數據傾斜的時候,不要急于提高executor的資源,修改參數或是修改程序,首先要檢查數據本身,是否存在異常數據。
1、數據問題造成的數據傾斜
找出異常的key
如果任務長時間卡在最后最后1個(幾個)任務,首先要對key進行抽樣分析,判斷是哪些key造成的。 選取key,對數據進行抽樣,統計出現的次數,根據出現次數大小排序取出前幾個。
比如: df.select(“key”).sample(false,0.1).(k=>(k,1)).reduceBykey(+).map(k=>(k._2,k._1)).sortByKey(false).take(10)
如果發現多數數據分布都較為平均,而個別數據比其他數據大上若干個數量級,則說明發生了數據傾斜。
經過分析,傾斜的數據主要有以下三種情況:
1、null(空值)或是一些無意義的信息()之類的,大多是這個原因引起。
2、無效數據,大量重復的測試數據或是對結果影響不大的有效數據。
3、有效數據,業務導致的正常數據分布。
解決辦法
第1,2種情況,直接對數據進行過濾即可(因為該數據對當前業務不會產生影響)。
第3種情況則需要進行一些特殊操作,常見的有以下幾種做法
(1) 隔離執行,將異常的key過濾出來單獨處理,最后與正常數據的處理結果進行union操作。
(2) 對key先添加隨機值,進行操作后,去掉隨機值,再進行一次操作。
(3) 使用reduceByKey 代替 groupByKey(reduceByKey用于對每個key對應的多個value進行merge操作,最重要的是它能夠在本地先進行merge操作,并且merge操作可以通過函數自定義.)
(4) 使用map join。
案例
如果使用reduceByKey因為數據傾斜造成運行失敗的問題。具體操作流程如下:
(1) 將原始的 key 轉化為 key + 隨機值(例如Random.nextInt)
(2) 對數據進行 reduceByKey(func)
(3) 將 key + 隨機值 轉成 key
(4) 再對數據進行 reduceByKey(func)
案例操作流程分析:
假設說有傾斜的Key,我們給所有的Key加上一個隨機數,然后進行reduceByKey操作;此時同一個Key會有不同的隨機數前綴,在進行reduceByKey操作的時候原來的一個非常大的傾斜的Key就分而治之變成若干個更小的Key,不過此時結果和原來不一樣,怎么破?進行map操作,目的是把隨機數前綴去掉,然后再次進行reduceByKey操作。(當然,如果你很無聊,可以再次做隨機數前綴),這樣我們就可以把原本傾斜的Key通過分而治之方案分散開來,最后又進行了全局聚合
注意1: 如果此時依舊存在問題,建議篩選出傾斜的數據單獨處理。最后將這份數據與正常的數據進行union即可。
注意2: 單獨處理異常數據時,可以配合使用Map Join解決。
2、spark使用不當造成的數據傾斜
提高shuffle并行度
dataFrame和sparkSql可以設置spark.sql.shuffle.partitions參數控制shuffle的并發度,默認為200。
rdd操作可以設置spark.default.parallelism控制并發度,默認參數由不同的Cluster Manager控制。
局限性: 只是讓每個task執行更少的不同的key。無法解決個別key特別大的情況造成的傾斜,如果某些key的大小非常大,即使一個task單獨執行它,也會受到數據傾斜的困擾。
使用map join 代替reduce join
在小表不是特別大(取決于你的executor大小)的情況下使用,可以使程序避免shuffle的過程,自然也就沒有數據傾斜的困擾了.(詳細見http://blog.csdn.net/lsshlsw/article/details/50834858、http://blog.csdn.net/lsshlsw/article/details/48694893)
局限性: 因為是先將小數據發送到每個executor上,所以數據量不能太大。
17.spark中map-side-join關聯優化:
將多份數據進行關聯是數據處理過程中非常普遍的用法,不過在分布式計算系統中,這個問題往往會變的非常麻煩,因為框架提供的 join 操作一般會將所有數據根據 key 發送到所有的 reduce 分區中去,也就是 shuffle 的過程。造成大量的網絡以及磁盤IO消耗,運行效率極其低下,這個過程一般被稱為 reduce-side-join。
如果其中有張表較小的話,我們則可以自己實現在 map 端實現數據關聯,跳過大量數據進行 shuffle 的過程,運行時間得到大量縮短,根據不同數據可能會有幾倍到數十倍的性能提升。
何時使用:在海量數據中匹配少量特定數據
原理:reduce-side-join 的缺陷在于會將key相同的數據發送到同一個partition中進行運算,大數據集的傳輸需要長時間的IO,同時任務并發度收到限制,還可能造成數據傾斜。
reduce-side-join 運行圖如下
map-side-join 運行圖如下:
將少量的數據轉化為Map進行廣播,廣播會將此 Map 發送到每個節點中,如果不進行廣播,每個task執行時都會去獲取該Map數據,造成了性能浪費。對大數據進行遍歷,使用mapPartition而不是map,因為mapPartition是在每個partition中進行操作,因此可以減少遍歷時新建broadCastMap.value對象的空間消耗,同時匹配不到的數據也不會返回。
18.kafka整合sparkStreaming問題:
(1)、如何實現sparkStreaming讀取kafka中的數據
可以這樣說:在kafka0.10版本之前有二種方式與sparkStreaming整合,一種是基于receiver,一種是direct,然后分別闡述這2種方式分別是什么
receiver:是采用了kafka高級api,利用receiver接收器來接受kafka topic中的數據,從kafka接收來的數據會存儲在spark的executor中,之后spark streaming提交的job會處理這些數據,kafka中topic的偏移量是保存在zk中的。
基本使用:
還有幾個需要注意的點:
在Receiver的方式中,Spark中的partition和kafka中的partition并不是相關的,所以如果我們加大每個topic的partition數量,僅僅是增加線程來處理由單一Receiver消費的主題。但是這并沒有增加Spark在處理數據上的并行度.
對于不同的Group和topic我們可以使用多個Receiver創建不同的Dstream來并行接收數據,之后可以利用union來統一成一個Dstream。
在默認配置下,這種方式可能會因為底層的失敗而丟失數據. 因為receiver一直在接收數據,在其已經通知zookeeper數據接收完成但是還沒有處理的時候,executor突然掛掉(或是driver掛掉通知executor關閉),緩存在其中的數據就會丟失. 如果希望做到高可靠, 讓數據零丟失,如果我們啟用了Write Ahead Logs(spark.streaming.receiver.writeAheadLog.enable=true)該機制會同步地將接收到的Kafka數據寫入分布式文件系統(比如HDFS)上的預寫日志中. 所以, 即使底層節點出現了失敗, 也可以使用預寫日志中的數據進行恢復. 復制到文件系統如HDFS,那么storage level需要設置成 StorageLevel.MEMORY_AND_DISK_SER,也就是KafkaUtils.createStream(…, StorageLevel.MEMORY_AND_DISK_SER)
direct:在spark1.3之后,引入了Direct方式。不同于Receiver的方式,Direct方式沒有receiver這一層,其會周期性的獲取Kafka中每個topic的每個partition中的最新offsets,之后根據設定的maxRatePerPartition來處理每個batch。(設置spark.streaming.kafka.maxRatePerPartition=10000。限制每秒鐘從topic的每個partition最多消費的消息條數)。
(2) 對比這2中方式的優缺點:
采用receiver方式:這種方式可以保證數據不丟失,但是無法保證數據只被處理一次,WAL實現的是At-least-once語義(至少被處理一次),如果在寫入到外部存儲的數據還沒有將offset更新到zookeeper就掛掉,這些數據將會被反復消費. 同時,降低了程序的吞吐量。
采用direct方式:相比Receiver模式而言能夠確保機制更加健壯. 區別于使用Receiver來被動接收數據, Direct模式會周期性地主動查詢Kafka, 來獲得每個topic+partition的最新的offset, 從而定義每個batch的offset的范圍. 當處理數據的job啟動時, 就會使用Kafka的簡單consumer api來獲取Kafka指定offset范圍的數據。
優點:
1、簡化并行讀取
如果要讀取多個partition, 不需要創建多個輸入DStream然后對它們進行union操作. Spark會創建跟Kafka partition一樣多的RDD partition, 并且會并行從Kafka中讀取數據. 所以在Kafka partition和RDD partition之間, 有一個一對一的映射關系.
2、高性能
如果要保證零數據丟失, 在基于receiver的方式中, 需要開啟WAL機制. 這種方式其實效率低下, 因為數據實際上被復制了兩份, Kafka自己本身就有高可靠的機制, 會對數據復制一份, 而這里又會復制一份到WAL中. 而基于direct的方式, 不依賴Receiver, 不需要開啟WAL機制, 只要Kafka中作了數據的復制, 那么就可以通過Kafka的副本進行恢復.
3、一次且僅一次的事務機制
基于receiver的方式, 是使用Kafka的高階API來在ZooKeeper中保存消費過的offset的. 這是消費Kafka數據的傳統方式. 這種方式配合著WAL機制可以保證數據零丟失的高可靠性, 但是卻無法保證數據被處理一次且僅一次, 可能會處理兩次. 因為Spark和ZooKeeper之間可能是不同步的. 基于direct的方式, 使用kafka的簡單api, Spark Streaming自己就負責追蹤消費的offset, 并保存在checkpoint中. Spark自己一定是同步的, 因此可以保證數據是消費一次且僅消費一次。不過需要自己完成將offset寫入zk的過程,在官方文檔中都有相應介紹.
*簡單代碼實例:
- messages.foreachRDD(rdd=>{
val message = rdd.map(_._2)//對數據進行一些操作
message.map(method)//更新zk上的offset (自己實現)
updateZKOffsets(rdd)
})
- sparkStreaming程序自己消費完成后,自己主動去更新zk上面的偏移量。也可以將zk中的偏移量保存在mysql或者redis數據庫中,下次重啟的時候,直接讀取mysql或者redis中的偏移量,獲取到上次消費的偏移量,接著讀取數據。
19.利用scala語言進行排序:
1.冒泡:
2.快讀排序:
20.spark master在使用zookeeper進行HA時,有哪些元數據保存在zookeeper?
答:spark通過這個參數spark.deploy.zookeeper.dir指定master元數據在zookeeper中保存的位置,包括worker,master,application,executors.standby節點要從zk中獲得元數據信息,恢復集群運行狀態,才能對外繼續提供服務,作業提交資源申請等,在恢復前是不能接受請求的,另外,master切換需要注意兩點:
1.在master切換的過程中,所有的已經在運行的程序皆正常運行,因為spark application在運行前就已經通過cluster manager獲得了計算資源,所以在運行時job本身的調度和處理master是沒有任何關系的;
2.在master的切換過程中唯一的影響是不能提交新的job,一方面不能提交新的應用程序給集群,因為只有Active master才能接受新的程序的提交請求,另外一方面,已經運行的程序也不能action操作觸發新的job提交請求。
21.spark master HA主從切換過程不會影響集群已有的作業運行,為什么?
答:因為程序在運行之前,已經向集群申請過資源,這些資源已經提交給driver了,也就是說已經分配好資源了,這是粗粒度分配,一次性分配好資源后不需要再關心資源分配,在運行時讓driver和executor自動交互,弊端是如果資源分配太多,任務運行完不會很快釋放,造成資源浪費,這里不適用細粒度分配的原因是因為任務提交太慢。
22.什么是粗粒度,什么是細粒度,各自的優缺點是什么?
答:1.粗粒度:啟動時就分配好資源,程序啟動,后續具體使用就使用分配好的資源,不需要再分配資源。好處:作業特別多時,資源復用率較高,使用粗粒度。缺點:容易資源浪費,如果一個job有1000個task,完成了999個,還有一個沒完成,那么使用粗粒度。如果有999個資源閑置在那里,會造成資源大量浪費。
2.細粒度:用資源的時候分配,用完了就立即回收資源,啟動會麻煩一點,啟動一次分配一次,會比較麻煩。
23.driver的功能是什么:
答:1.一個spark作業運行時包括一個driver進程,也就是作業的主進程,具有main函數,并且有sparkContext的實例,是程序的入口;
2.功能:負責向集群申請資源,向master注冊信息,負責了作業的調度,負責了作業的解析,生成stage并調度task到executor上,包括DAGScheduler,TaskScheduler。
24.spark的有幾種部署模式,每種模式特點?
1)本地模式
Spark不一定非要跑在hadoop集群,可以在本地,起多個線程的方式來指定。將Spark應用以多線程的方式直接運行在本地,一般都是為了方便調試,本地模式分三類
· local:只啟動一個executor
· local[k]:啟動k個executor
· local:啟動跟cpu數目相同的 executor
2)standalone模式
分布式部署集群, 自帶完整的服務,資源管理和任務監控是Spark自己監控,這個模式也是其他模式的基礎,
3)Spark on yarn模式
分布式部署集群,資源和任務監控交給yarn管理,但是目前僅支持粗粒度資源分配方式,包含cluster和client運行模式,cluster適合生產,driver運行在集群子節點,具有容錯功能,client適合調試,dirver運行在客戶端
4)Spark On Mesos模式。官方推薦這種模式(當然,原因之一是血緣關系)。正是由于Spark開發之初就考慮到支持Mesos,因此,目前而言,Spark運行在Mesos上會比運行在YARN上更加靈活,更加自然。用戶可選擇兩種調度模式之一運行自己的應用程序:
粗粒度模式(Coarse-grained Mode):每個應用程序的運行環境由一個Dirver和若干個Executor組成,其中,每個Executor占用若干資源,內部可運行多個Task(對應多少個“slot”)。應用程序的各個任務正式運行之前,需要將運行環境中的資源全部申請好,且運行過程中要一直占用這些資源,即使不用,最后程序運行結束后,回收這些資源。
細粒度模式(Fine-grained Mode):鑒于粗粒度模式會造成大量資源浪費,Spark On Mesos還提供了另外一種調度模式:細粒度模式,這種模式類似于現在的云計算,思想是按需分配。
25.Spark技術棧有哪些組件,每個組件都有什么功能,適合什么應用場景?
1)Spark core:是其它組件的基礎,spark的內核,主要包含:有向循環圖、RDD、Lingage、Cache、broadcast等,并封裝了底層通訊框架,是Spark的基礎。
2)SparkStreaming是一個對實時數據流進行高通量、容錯處理的流式處理系統,可以對多種數據源(如Kdfka、Flume、Twitter、Zero和TCP 套接字)進行類似Map、Reduce和Join等復雜操作,將流式計算分解成一系列短小的批處理作業。
3)Spark sql:Shark是SparkSQL的前身,Spark SQL的一個重要特點是其能夠統一處理關系表和RDD,使得開發人員可以輕松地使用SQL命令進行外部查詢,同時進行更復雜的數據分析
4)BlinkDB :是一個用于在海量數據上運行交互式 SQL 查詢的大規模并行查詢引擎,它允許用戶通過權衡數據精度來提升查詢響應時間,其數據的精度被控制在允許的誤差范圍內。
5)MLBase是Spark生態圈的一部分專注于機器學習,讓機器學習的門檻更低,讓一些可能并不了解機器學習的用戶也能方便地使用MLbase。MLBase分為四部分:MLlib,MLI、ML Optimizer和MLRuntime。
6)GraphX是Spark中用于圖和圖并行計算
26.spark中worker 的主要工作是什么?
主要功能:管理當前節點內存,CPU的使用情況,接受master發送過來的資源指令,通過executorRunner啟動程序分配任務,worker就類似于包工頭,管理分配新進程,做計算的服務,相當于process服務,需要注意的是:
1.worker會不會匯報當前信息給master?worker心跳給master主要只有workid,不會以心跳的方式發送資源信息給master,這樣master就知道worker是否存活,只有故障的時候才會發送資源信息;
2.worker不會運行代碼,具體運行的是executor,可以運行具體application斜的業務邏輯代碼,操作代碼的節點,不會去運行代碼。
27.簡單說一下hadoop和spark的shuffle相同和差異?
答:1)從 high-level 的角度來看,兩者并沒有大的差別。 都是將 mapper(Spark 里是 ShuffleMapTask)的輸出進行 partition,不同的 partition 送到不同的 reducer(Spark 里 reducer 可能是下一個 stage 里的 ShuffleMapTask,也可能是 ResultTask)。Reducer 以內存作緩沖區,邊 shuffle 邊 aggregate 數據,等到數據 aggregate 好以后進行 reduce() (Spark 里可能是后續的一系列操作)。
2)從 low-level 的角度來看,兩者差別不小。 Hadoop MapReduce 是 sort-based,進入 combine() 和 reduce() 的 records 必須先 sort。這樣的好處在于 combine/reduce() 可以處理大規模的數據,因為其輸入數據可以通過外排得到(mapper 對每段數據先做排序,reducer 的 shuffle 對排好序的每段數據做歸并)。目前的 Spark 默認選擇的是 hash-based,通常使用 HashMap 來對 shuffle 來的數據進行 aggregate,不會對數據進行提前排序。如果用戶需要經過排序的數據,那么需要自己調用類似 sortByKey() 的操作;如果你是Spark 1.1的用戶,可以將spark.shuffle.manager設置為sort,則會對數據進行排序。在Spark 1.2中,sort將作為默認的Shuffle實現。
3)從實現角度來看,兩者也有不少差別。 Hadoop MapReduce 將處理流程劃分出明顯的幾個階段:map(), spill, merge, shuffle, sort, reduce() 等。每個階段各司其職,可以按照過程式的編程思想來逐一實現每個階段的功能。在 Spark 中,沒有這樣功能明確的階段,只有不同的 stage 和一系列的 transformation(),所以 spill, merge, aggregate 等操作需要蘊含在 transformation() 中。
如果我們將 map 端劃分數據、持久化數據的過程稱為 shuffle write,而將 reducer 讀入數據、aggregate 數據的過程稱為 shuffle read。那么在 Spark 中,問題就變為怎么在 job 的邏輯或者物理執行圖中加入 shuffle write 和 shuffle read 的處理邏輯?以及兩個處理邏輯應該怎么高效實現?
Shuffle write由于不要求數據有序,shuffle write 的任務很簡單:將數據 partition 好,并持久化。之所以要持久化,一方面是要減少內存存儲空間壓力,另一方面也是為了 fault-tolerance。
28.Mapreduce和Spark的都是并行計算,那么他們有什么相同和區別
答:兩者都是用mr模型來進行并行計算:
1)hadoop的一個作業稱為job,job里面分為map task和reduce task,每個task都是在自己的進程中運行的,當task結束時,進程也會結束。
2)spark用戶提交的任務成為application,一個application對應一個sparkcontext,app中存在多個job,每觸發一次action操作就會產生一個job。這些job可以并行或串行執行,每個job中有多個stage,stage是shuffle過程中DAGSchaduler通過RDD之間的依賴關系劃分job而來的,每個stage里面有多個task,組成taskset有TaskSchaduler分發到各個executor中執行,executor的生命周期是和app一樣的,即使沒有job運行也是存在的,所以task可以快速啟動讀取內存進行計算。
3)hadoop的job只有map和reduce操作,表達能力比較欠缺而且在mr過程中會重復的讀寫hdfs,造成大量的io操作,多個job需要自己管理關系。
spark的迭代計算都是在內存中進行的,API中提供了大量的RDD操作如join,groupby等,而且通過DAG圖可以實現良好的容錯。
29.RDD機制?
答:rdd分布式彈性數據集,簡單的理解成一種數據結構,是spark框架上的通用貨幣。
所有算子都是基于rdd來執行的,不同的場景會有不同的rdd實現類,但是都可以進行互相轉換。
rdd執行過程中會形成dag圖,然后形成lineage保證容錯性等。 從物理的角度來看rdd存儲的是block和node之間的映射。
30、spark有哪些組件?
答:主要有如下組件:
1)master:管理集群和節點,不參與計算。
2)worker:計算節點,進程本身不參與計算,和master匯報。
3)Driver:運行程序的main方法,創建spark context對象。
4)spark context:控制整個application的生命周期,包括dagsheduler和task scheduler等組件。
5)client:用戶提交程序的入口。
31、spark工作機制?
答:用戶在client端提交作業后,會由Driver運行main方法并創建spark context上下文。
執行add算子,形成dag圖輸入dagscheduler,按照add之間的依賴關系劃分stage輸入task scheduler。 task scheduler會將stage劃分為task set分發到各個節點的executor中執行。
32、spark的優化怎么做?
答: spark調優比較復雜,但是大體可以分為三個方面來進行,
1)平臺層面的調優:防止不必要的jar包分發,提高數據的本地性,選擇高效的存儲格式如parquet,
2)應用程序層面的調優:過濾操作符的優化降低過多小任務,降低單條記錄的資源開銷,處理數據傾斜,復用RDD進行緩存,作業并行化執行等等,
3)JVM層面的調優:設置合適的資源量,設置合理的JVM,啟用高效的序列化方法如kyro,增大off head內存等等
序列化在分布式系統中扮演著重要的角色,優化Spark程序時,首當其沖的就是對序列化方式的優化。Spark為使用者提供兩種序列化方式:
Java serialization: 默認的序列化方式。
Kryo serialization: 相較于 Java serialization 的方式,速度更快,空間占用更小,但并不支持所有的序列化格式,同時使用的時候需要注冊class。spark-sql中默認使用的是kyro的序列化方式。
可以在spark-default.conf設置全局參數,也可以代碼中初始化時對SparkConf設置 conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer") ,該參數會同時作用于機器之間數據的shuffle操作以及序列化rdd到磁盤,內存。
Spark不將Kyro設置成默認的序列化方式是因為它需要對類進行注冊,官方強烈建議在一些網絡數據傳輸很大的應用中使用kyro序列化。
如果你要序列化的對象比較大,可以增加參數spark.kryoserializer.buffer所設置的值。
如果你沒有注冊需要序列化的class,Kyro依然可以照常工作,但會存儲每個對象的全類名(full class name),這樣的使用方式往往比默認的 Java serialization 還要浪費更多的空間。
可以設置 spark.kryo.registrationRequired 參數為 true,使用kyro時如果在應用中有類沒有進行注冊則會報錯: