Spark面試,Spark面試題,Spark面試彙總

Table of Contents

1、你覺得spark 可以完全替代hadoop 麼?

2、Spark消費 Kafka,分佈式的情況下,如何保證消息的順序?

3、對於 Spark 中的數據傾斜問題你有什麼好的方案?

4、你所理解的 Spark 的 shuffle 過程?

5、Spark有哪些聚合類的算子,我們應該儘量避免什麼類型的算子?

6、spark on yarn 作業執行流程,yarn-client 和 yarn cluster 有什麼區別

7、Spark爲什麼快,Spark SQL 一定比 Hive 快嗎

8、RDD, DAG, Stage怎麼理解?

9、RDD 如何通過記錄更新的方式容錯

10、寬依賴、窄依賴怎麼理解?

11、Job 和 Task 怎麼理解

​12、Spark 血統的概念

13、任務的概念

14、容錯方法

15、Spark 粗粒度和細粒度

16、Spark優越性

17、Transformation和action是什麼?區別?舉幾個常用方法

18、Spark作業提交流程是怎麼樣的

19、Spark streamning工作流程是怎麼樣的,和Storm比有什麼區別

​20、Spark 機器學習和 Spark 圖計算接觸過沒有,舉例說明你用它做過什麼?

21、Spark RDD是怎麼容錯的,基本原理是什麼?

22、爲什麼要用Yarn來部署Spark?

23、說說yarn-cluster和yarn-client的異同點。

24、解釋一下 groupByKey, reduceByKey 還有 reduceByKeyLocally

25、說說 persist() 和 cache() 的異同

26、可以解釋一下這兩段程序的異同嗎

27、說說map和mapPartitions的區別

28、groupByKey和reduceByKey是屬於Transformation還是 Action?

29、說說Spark支持的3種集羣管理器

30、說說Worker和Excutor的異同

31、說說Spark提供的兩種共享變量

32、說說檢查點的意義

33、說說Spark的高可用和容錯

34、解釋一下Spark Master的選舉過程

35、說說Spark如何實現序列化組件的

36、說說對Master的理解

37、說說什麼是窗口間隔和滑動間隔

38、Spark Streaming小文件問題

39、Spark的UDF?

40、Mesos下粗粒度和細粒度對比?

41、Spark Local和Standalone有什麼區別

42、說說SparkContext和SparkSession有什麼區別?

43、如果Spark Streaming停掉了,如何保證Kafka的重新運作是合理的呢

44、列舉Spark中 Transformation 和 Action算子

45、Spark經常說的Repartition是個什麼玩意

46、Spark Streaming Duration的概念

47、簡單寫一個WordCount程序

48、說說Yarn-cluster的運行階段

49、Mesos粗細度對比

50、說說Standalone模式下運行Spark程序的大概流程

51、如何區分 Appliction(應用程序)還有 Driver(驅動程序)

52、介紹一下 Spark 通信的啓動方式

53、介紹一下 Spark 運行時候的消息通信

54、解釋一下Stage

55、描述一下Worker異常的情況

56、描述一下Master異常的情況

57、Spark的存儲體系

​58、簡述Spark Streaming

59、知道 Hadoop MRv1 的侷限嗎

60、說說Spark的特點,相對於MR來說

61、說說Spark Narrow Dependency的分類

62、Task和Stage的分類

63、Spark的編程模型

64、Spark的計算模型

65、總述Spark的架構

66、一句話說說 Spark Streaming 是如何收集和處理數據的

67、解釋一下窗口間隔window duration和滑動間隔slide duration

68、介紹一下Spark Streaming的foreachRDD(func)方法

69、簡單描述一下Spark Streaming的容錯原理

70、DStream 有幾種轉換操作

71、聊聊Spark Streaming的運行架構

​72、說說DStreamGraph

73、創建RDD的方式以及如何繼承創建RDD

74、分析一下Spark Streaming的transform()和updateStateByKey()兩個操作

75、說說Spark Streaming的輸出操作

76、談談Spark Streaming Driver端重啓會發生什麼

77、再談Spark Streaming的容錯性

78、流數據如何存儲

79、StreamingContext啓動時序圖嗎

80、說說RDD和DataFrame和DataSet的關係


1、你覺得spark 可以完全替代hadoop 麼?

Spark 會替代 MR,Spark 存儲依賴 HDFS,資源調度依賴 YARN,集羣管理依賴 Zookeeper。

2、Spark消費 Kafka,分佈式的情況下,如何保證消息的順序?

Kafka 分佈式的單位是 Partition。如何保證消息有序,需要分幾個情況討論。

  • 同一個 Partition 用一個 write ahead log 組織,所以可以保證 FIFO 的順序。

  • 不同 Partition 之間不能保證順序。但是絕大多數用戶都可以通過 message key 來定義,因爲同一個 key 的 message 可以保證只發送到同一個 Partition。比如說 key 是 user id,table row id 等等,所以同一個 user 或者同一個 record 的消息永遠只會發送到同一個 Partition上,保證了同一個 user 或 record 的順序。

  • 當然,如果你有 key skewness 就有些麻煩,需要特殊處理。

實際情況中: (1)不關注順序的業務大量存在;(2)隊列無序不代表消息無序。

第(2)條的意思是說::我們不保證隊列的全局有序,但可以保證消息的局部有序。舉個例子: 保證來自同1個 order id 的消息,是有序的!

Kafka 中發送1條消息的時候,可以指定(topic, partition, key) 3個參數。partiton 和 key 是可選的。如果你指定了 partition,那就是所有消息發往同1個 partition,就是有序的。並且在消費端,Kafka 保證,1個 partition 只能被1個 consumer 消費。或者你指定 key(比如 order id),具有同1個 key 的所有消息,會發往同1個 partition。也是有序的。

3、對於 Spark 中的數據傾斜問題你有什麼好的方案?

簡單一句:Spark 數據傾斜的幾種場景以及對應的解決方案,包括避免數據源傾斜,調整並行度,使用自定義 Partitioner,使用 Map 側 Join 代替 Reduce 側 Join(內存表合併),給傾斜 Key 加上隨機前綴等。

什麼是數據傾斜 對 Spark/Hadoop 這樣的大數據系統來講,數據量大並不可怕,可怕的是數據傾斜。數據傾斜指的是,並行處理的數據集中,某一部分(如 Spark 或 Kafka 的一個 Partition)的數據顯著多於其它部分,從而使得該部分的處理速度成爲整個數據集處理的瓶頸(木桶效應)。

數據傾斜是如何造成的 在 Spark 中,同一個 Stage 的不同 Partition 可以並行處理,而具有依賴關係的不同 Stage 之間是串行處理的。假設某個 Spark Job 分爲 Stage 0和 Stage 1兩個 Stage,且 Stage 1依賴於 Stage 0,那 Stage 0完全處理結束之前不會處理Stage 1。而 Stage 0可能包含 N 個 Task,這 N 個 Task 可以並行進行。如果其中 N-1個 Task 都在10秒內完成,而另外一個 Task 卻耗時1分鐘,那該 Stage 的總時間至少爲1分鐘。換句話說,一個 Stage 所耗費的時間,主要由最慢的那個 Task 決定。由於同一個 Stage 內的所有 Task 執行相同的計算,在排除不同計算節點計算能力差異的前提下,不同 Task 之間耗時的差異主要由該 Task 所處理的數據量決定。

具體解決方案 :

1. 調整並行度分散同一個 Task 的不同 Key:Spark 在做 Shuffle 時,默認使用 HashPartitioner對數據進行分區。如果並行度設置的不合適,可能造成大量不相同的 Key 對應的數據被分配到了同一個 Task 上,造成該 Task 所處理的數據遠大於其它 Task,從而造成數據傾斜。如果調整 Shuffle 時的並行度,使得原本被分配到同一 Task 的不同 Key 發配到不同 Task 上處理,則可降低原 Task 所需處理的數據量,從而緩解數據傾斜問題造成的短板效應。

2. 自定義Partitioner:使用自定義的 Partitioner(默認爲 HashPartitioner),將原本被分配到同一個 Task 的不同 Key 分配到不同 Task,可以拿上圖繼續想象一下,通過自定義 Partitioner 可以把原本分到 Task0 的 Key 分到 Task1,那麼 Task0 的要處理的數據量就少了。 

3. 將 Reduce side(側) Join 轉變爲 Map side(側) Join:通過 Spark 的 Broadcast 機制,將 Reduce 側 Join 轉化爲 Map 側 Join,避免 Shuffle 從而完全消除 Shuffle 帶來的數據傾斜。可以看到 RDD2 被加載到內存中了。

4. 爲 skew 的 key 增加隨機前/後綴:爲數據量特別大的 Key 增加隨機前/後綴,使得原來 Key 相同的數據變爲 Key 不相同的數據,從而使傾斜的數據集分散到不同的 Task 中,徹底解決數據傾斜問題。Join 另一則的數據中,與傾斜 Key 對應的部分數據,與隨機前綴集作笛卡爾乘積,從而保證無論數據傾斜側傾斜 Key 如何加前綴,都能與之正常 Join。

5. 大表隨機添加 N 種隨機前綴,小表擴大 N 倍:如果出現數據傾斜的 Key 比較多,上一種方法將這些大量的傾斜 Key 分拆出來,意義不大(很難一個 Key 一個 Key 都加上後綴)。此時更適合直接對存在數據傾斜的數據集全部加上隨機前綴,然後對另外一個不存在嚴重數據傾斜的數據集整體與隨機前綴集作笛卡爾乘積(即將數據量擴大 N 倍),可以看到 RDD2 擴大了 N 倍了,再和加完前綴的大數據做笛卡爾積。

4、你所理解的 Spark 的 shuffle 過程?

Spark shuffle 處於一個寬依賴,可以實現類似混洗的功能,將相同的 Key 分發至同一個 Reducer上進行處理。

5、Spark有哪些聚合類的算子,我們應該儘量避免什麼類型的算子?

在我們的開發過程中,能避免則儘可能避免使用 reduceByKey、join、distinct、repartition 等會進行 shuffle 的算子,儘量使用 map 類的非 shuffle 算子。這樣的話,沒有 shuffle 操作或者僅有較少 shuffle 操作的 Spark 作業,可以大大減少性能開銷。

6、spark on yarn 作業執行流程,yarn-client 和 yarn cluster 有什麼區別

Spark On Yarn 的優勢 

1. Spark 支持資源動態共享,運行於 Yarn 的框架都共享一個集中配置好的資源池 

2. 可以很方便的利用 Yarn 的資源調度特性來做分類·,隔離以及優先級控制負載,擁有更靈活的調度策略 

3. Yarn 可以自由地選擇 executor 數量 

4. Yarn 是唯一支持 Spark 安全的集羣管理器,使用 Yarn,Spark 可以運行於 Kerberos Hadoop 之上,在它們進程之間進行安全認證

yarn-client 和 yarn cluster 的異同 :

1. 從廣義上講,yarn-cluster 適用於生產環境。而 yarn-client 適用於交互和調試,也就是希望快速地看到 application 的輸出。

2. 從深層次的含義講,yarn-cluster 和 yarn-client 模式的區別其實就是 Application Master 進程的區別,yarn-cluster 模式下,driver 運行在 AM(Application Master)中,它負責向 YARN 申請資源,並監督作業的運行狀況。當用戶提交了作業之後,就可以關掉 Client,作業會繼續在 YARN 上運行。然而 yarn-cluster 模式不適合運行交互類型的作業。而 yarn-client 模式下,Application Master 僅僅向 YARN 請求 executor,Client 會和請求的 container 通信來調度他們工作,也就是說 Client 不能離開。

7、Spark爲什麼快,Spark SQL 一定比 Hive 快嗎

Spark SQL 比 Hadoop Hive 快,是有一定條件的,而且不是 Spark SQL 的引擎比 Hive 的引擎快,相反,Hive 的 HQL 引擎還比 Spark SQL 的引擎更快。其實,關鍵還是在於 Spark 本身快。

  1. 消除了冗餘的 HDFS 讀寫:Hadoop 每次 shuffle 操作後,必須寫到磁盤,而 Spark 在 shuffle 後不一定落盤,可以 cache 到內存中,以便迭代時使用。如果操作複雜,很多的 shufle 操作,那麼 Hadoop 的讀寫 IO 時間會大大增加,也是 Hive 更慢的主要原因了。

  2. 消除了冗餘的 MapReduce 階段:Hadoop 的 shuffle 操作一定連着完整的 MapReduce 操作,冗餘繁瑣。而 Spark 基於 RDD 提供了豐富的算子操作,且 reduce 操作產生 shuffle 數據,可以緩存在內存中。

  3. JVM 的優化:Hadoop 每次 MapReduce 操作,啓動一個 Task 便會啓動一次 JVM,基於進程的操作。而 Spark 每次 MapReduce 操作是基於線程的,只在啓動 Executor 是啓動一次 JVM,內存的 Task 操作是在線程複用的。每次啓動 JVM 的時間可能就需要幾秒甚至十幾秒,那麼當 Task 多了,這個時間 Hadoop 不知道比 Spark 慢了多少。

記住一種反例 考慮一種極端查詢:

Select month_id, sum(sales) from T group by month_id;

這個查詢只有一次 shuffle 操作,此時,也許 Hive HQL 的運行時間也許比 Spark 還快,反正 shuffle 完了都會落一次盤,或者都不落盤。

結論 Spark 快不是絕對的,但是絕大多數,Spark 都比 Hadoop 計算要快。這主要得益於其對 mapreduce 操作的優化以及對 JVM 使用的優化。

8、RDD, DAG, Stage怎麼理解?

DAG:Spark 中使用 DAG 對 RDD 的關係進行建模,描述了 RDD 的依賴關係,這種關係也被稱之爲 lineage(血緣),RDD 的依賴關係使用 Dependency 維護。DAG 在 Spark 中的對應的實現爲 DAGScheduler。

RDD:RDD 是 Spark 的靈魂,也稱爲彈性分佈式數據集。一個 RDD 代表一個可以被分區的只讀數據集。RDD 內部可以有許多分區(partitions),每個分區又擁有大量的記錄(records)。

Rdd的五個特徵:

1. dependencies:建立 RDD 的依賴關係,主要 RDD 之間是寬窄依賴的關係,具有窄依賴關係的 RDD 可以在同一個 stage 中進行計算。

2. partition:一個 RDD 會有若干個分區,分區的大小決定了對這個 RDD 計算的粒度,每個 RDD 的分區的計算都在一個單獨的任務中進行。

3. preferedlocations:按照“移動數據不如移動計算”原則,在 Spark 進行任務調度的時候,優先將任務分配到數據塊存儲的位置。

4. compute:Spark 中的計算都是以分區爲基本單位的,compute 函數只是對迭代器進行復合,並不保存單次計算的結果。

5. partitioner: 只存在於(K,V)類型的 RDD 中,非(K,V)類型的 partitioner 的值就是 None。

RDD 的算子主要分成2類,action 和 transformation。這裏的算子概念,可以理解成就是對數據集的變換。action 會觸發真正的作業提交,而 transformation 算子是不會立即觸發作業提交的。每一個 transformation 方法返回一個新的 RDD。只是某些 transformation 比較複雜,會包含多個子 transformation,因而會生成多個 RDD。這就是實際 RDD 個數比我們想象的多一些 的原因。通常是,當遇到 action 算子時會觸發一個job的提交,然後反推回去看前面的 transformation 算子,進而形成一張有向無環圖。

Stage 在 DAG 中又進行 stage 的劃分,劃分的依據是依賴是否是 shuffle 的,每個 stage 又可以劃分成若干 task。接下來的事情就是 driver 發送 task 到 executor,executor 自己的線程池去執行這些 task,完成之後將結果返回給 driver。action 算子是劃分不同 job 的依據。

9、RDD 如何通過記錄更新的方式容錯

RDD 的容錯機制實現分佈式數據集容錯方法有兩種:1. 數據檢查點 2. 記錄更新。

RDD 採用記錄更新的方式:記錄所有更新點的成本很高。所以,RDD只支持粗顆粒變換,即只記錄單個塊(分區)上執行的單個操作,然後創建某個 RDD 的變換序列(血統 lineage)存儲下來;變換序列指,每個 RDD 都包含了它是如何由其他 RDD 變換過來的以及如何重建某一塊數據的信息。因此 RDD 的容錯機制又稱“血統”容錯。

10、寬依賴、窄依賴怎麼理解?

窄依賴指的是每一個 parent RDD 的 partition 最多被子 RDD 的一個 partition 使用(一子一親)。

寬依賴指的是多個子 RDD 的 partition 會依賴同一個 parent RDD的 partition(多子一親)。

RDD 作爲數據結構,本質上是一個只讀的分區記錄集合。一個 RDD 可以包含多個分區,每個分區就是一個 dataset 片段。RDD 可以相互依賴。

首先,窄依賴可以支持在同一個 cluster node上,以 pipeline 形式執行多條命令(也叫同一個 stage 的操作),例如在執行了 map 後,緊接着執行 filter。相反,寬依賴需要所有的父分區都是可用的,可能還需要調用類似 MapReduce 之類的操作進行跨節點傳遞。

其次,則是從失敗恢復的角度考慮。窄依賴的失敗恢復更有效,因爲它只需要重新計算丟失的 parent partition 即可,而且可以並行地在不同節點進行重計算(一臺機器太慢就會分配到多個節點進行),相反,寬依賴牽涉 RDD 各級的多個 parent partition。

11、Job 和 Task 怎麼理解

Job:Spark 的 Job 來源於用戶執行 action 操作(這是 Spark 中實際意義的 Job),就是從 RDD 中獲取結果的操作,而不是將一個 RDD 轉換成另一個 RDD 的 transformation 操作。

Task:一個 Stage 內,最終的 RDD 有多少個 partition,就會產生多少個 task。看一看圖就明白了,可以數一數每個 Stage 有多少個 Task。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

 

 

https://mmbiz.qpic.cn/mmbiz_png/UdK9ByfMT2OSwS8tHQeMicc0egREicTZ5RScIjsWSWADIAVBvBZ5NuKxvnbQnFljGSdib0IeCLklf7BleljHhjmWw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1
12、Spark 血統的概念

RDD 的 lineage 記錄的是粗顆粒度的特定數據轉換(transformation)操作(filter, map, join etc.)行爲。當這個 RDD 的部分分區數據丟失時,它可以通過 lineage 獲取足夠的信息來重新運算和恢復丟失的數據分區。這種粗顆粒的數據模型,限制了 Spark 的運用場合,但同時相比細顆粒度的數據模型,也帶來了性能的提升。

13、任務的概念

包含很多 task 的並行計算,可以認爲是 Spark RDD 裏面的 action,每個 action 的計算會生成一個 job。用戶提交的 job 會提交給 DAGScheduler,job 會被分解成 Stage 和 Task。


14、容錯方法

Spark 選擇記錄更新的方式。但是,如果更新粒度太細太多,那麼記錄更新成本也不低。因此,RDD只支持粗粒度轉換,即只記錄單個塊上執行的單個操作,然後將創建 RDD 的一系列變換序列(每個 RDD 都包含了他是如何由其他 RDD 變換過來的以及如何重建某一塊數據的信息。因此 RDD 的容錯機制又稱血統容錯)記錄下來,以便恢復丟失的分區。lineage本質上很類似於數據庫中的重做日誌(Redo Log),只不過這個重做日誌粒度很大,是對全局數據做同樣的重做進而恢復數據。

相比其他系統的細顆粒度的內存數據更新級別的備份或者 LOG 機制,RDD 的 lineage 記錄的是粗顆粒度的特定數據 transformation 操作行爲。當這個 RDD 的部分分區數據丟失時,它可以通過 lineage 獲取足夠的信息來重新運算和恢復丟失的數據分區。


15、Spark 粗粒度和細粒度

Spark 中,每個 application 對應一個 SparkContext。對於 SparkContext 之間的調度關係,取決於 Spark 的運行模式。對 Standalone 模式而言,Spark Master 節點先計算集羣內的計算資源能否滿足等待隊列中的應用對內存和 CPU 資源的需求,如果可以,則 Master 創建 Spark Driver,啓動應用的執行。宏觀上來講,這種對應用的調度類似於 FIFO 策略。在 Mesos 和 Yarn 模式下,底層的資源調度系統的調度策略都是由 Mesos 和 Yarn 決定的。具體分類描述如下:

  1. Standalone 模式:默認以用戶提交 Applicaiton 的順序來調度,即 FIFO 策略。每個應用執行時獨佔所有資源。如果有多個用戶要共享集羣資源,則可以使用參數 spark.cores.max 來配置應用在集羣中可以使用的最大 CPU 核的數量。如果不配置,則採用默認參數 spark.deploy.defaultCore 的值來確定。

  2. Mesos 模式:如果在 Mesos 上運行 Spark,用戶想要靜態配置資源的話,可以設置 spark.mesos.coarse 爲 true,這樣 Mesos 變爲粗粒度調度模式。然後可以設置 spark.cores.max 指定集羣中可以使用的最大核數,與上面 Standalone 模式類似。同時,在 Mesos 模式下,用戶還可以設置參數 spark.executor.memory 來配置每個 executor 的內存使用量。如果想使 Mesos 在細粒度模式下運行,可以通過 mesos://<url-info> 設置動態共享 CPU core 的執行模式。在這種模式下,應用不執行時的空閒 CPU 資源得以被其他用戶使用,提升了 CPU 使用率。

16、Spark優越性

一、Spark 的5大優勢:

1. 更高的性能。因爲數據被加載到集羣主機的分佈式內存中。數據可以被快速的轉換迭代,並緩存用以後續的頻繁訪問需求。在數據全部加載到內存的情況下,Spark可以比Hadoop快100倍,在內存不夠存放所有數據的情況下快hadoop10倍。

2. 通過建立在Java,Scala,Python,SQL(應對交互式查詢)的標準API以方便各行各業使用,同時還含有大量開箱即用的機器學習庫。

3. 與現有Hadoop 1和2.x(YARN)生態兼容,因此機構可以無縫遷移。

4. 方便下載和安裝。方便的shell(REPL: Read-Eval-Print-Loop)可以對API進行交互式的學習。

5. 藉助高等級的架構提高生產力,從而可以講精力放到計算上。

二、MapReduce與Spark相比,有哪些異同點:

1、基本原理上:(1) MapReduce:基於磁盤的大數據批量處理系統 (2)Spark:基於RDD(彈性分佈式數據集)數據處理,顯示將RDD數據存儲到磁盤和內存中。

2、模型上:(1) MapReduce可以處理超大規模的數據,適合日誌分析挖掘等較少的迭代的長任務需求,結合了數據的分佈式的計算。(2) Spark:適合數據的挖掘,機器學習等多輪迭代式計算任務。

17、Transformation和action是什麼?區別?舉幾個常用方法

RDD 創建後就可以在 RDD 上進行數據處理。RDD 支持兩種操作:1. 轉換(transformation): 即從現有的數據集創建一個新的數據集 2. 動作(action): 即在數據集上進行計算後,返回一個值給 Driver 程序

RDD 的轉化操作是返回一個新的 RDD 的操作,比如 map() 和 filter() ,而行動操作則是向驅動器程序返回結果或把結果寫入外部系統的操作,會觸發實際的計算,比如 count() 和 first() 。Spark 對待轉化操作和行動操作的方式很不一樣,因此理解你正在進行的操作的類型是很重要的。如果對於一個特定的函數是屬於轉化操作還是行動操作感到困惑,你可以看看它的返回值類型:轉化操作返回的是 RDD,而行動操作返回的是其他的數據類型。

RDD 中所有的 Transformation 都是惰性的,也就是說,它們並不會直接計算結果。相反的它們只是記住了這些應用到基礎數據集(例如一個文件)上的轉換動作。只有當發生一個要求返回結果給 Driver 的 Action 時,這些 Transformation 纔會真正運行。

這個設計讓 Spark 更加有效的運行。

18、Spark作業提交流程是怎麼樣的

  • spark-submit 提交代碼,執行 new SparkContext(),在 SparkContext 裏構造 DAGScheduler 和 TaskScheduler

  • TaskScheduler 會通過後臺的一個進程,連接 Master,向 Master 註冊 Application。

  • Master 接收到 Application 請求後,會使用相應的資源調度算法,在 Worker 上爲這個 Application 啓動多個 Executer。

  • Executor 啓動後,會自己反向註冊到 TaskScheduler 中。所有 Executor 都註冊到 Driver 上之後,SparkContext 結束初始化,接下來往下執行我們自己的代碼。

  • 每執行到一個 Action,就會創建一個 Job。Job 會提交給 DAGScheduler。

  • DAGScheduler 會將 Job劃分爲多個 stage,然後每個 stage 創建一個 TaskSet。

  • TaskScheduler 會把每一個 TaskSet 裏的 Task,提交到 Executor 上執行。

  • Executor 上有線程池,每接收到一個 Task,就用 TaskRunner 封裝,然後從線程池裏取出一個線程執行這個 task。(TaskRunner 將我們編寫的代碼,拷貝,反序列化,執行 Task,每個 Task 執行 RDD 裏的一個 partition)

19、Spark streamning工作流程是怎麼樣的,和Storm比有什麼區別

Spark Streaming 與 Storm 都可以用於進行實時流計算。但是他們兩者的區別是非常大的。其中區別之一,就是,Spark Streaming 和 Storm 的計算模型完全不一樣,Spark Streaming 是基於 RDD 的,因此需要將一小段時間內的,比如1秒內的數據,收集起來,作爲一個 RDD,然後再針對這個 batch 的數據進行處理。而 Storm 卻可以做到每來一條數據,都可以立即進行處理和計算。因此,Spark Streaming 實際上嚴格意義上來說,只能稱作準實時的流計算框架;而 Storm 是真正意義上的實時計算框架。此外,Storm 支持的一項高級特性,是 Spark Streaming 暫時不具備的,即 Storm 支持在分佈式流式計算程序(Topology)在運行過程中,可以動態地調整並行度,從而動態提高併發處理能力。而 Spark Streaming 是無法動態調整並行度的。但是 Spark Streaming 也有其優點,首先 Spark Streaming 由於是基於 batch 進行處理的,因此相較於 Storm 基於單條數據進行處理,具有數倍甚至數十倍的吞吐量。此外,Spark Streaming 由於也身處於 Spark 生態圈內,因此Spark Streaming可以與Spark Core、Spark SQL,甚至是Spark MLlib、Spark GraphX進行無縫整合。流式處理完的數據,可以立即進行各種map、reduce轉換操作,可以立即使用sql進行查詢,甚至可以立即使用machine learning或者圖計算算法進行處理。這種一站式的大數據處理功能和優勢,是 Storm 無法匹敵的。因此,綜合上述來看,通常在對實時性要求特別高,而且實時數據量不穩定,比如在白天有高峯期的情況下,可以選擇使用 Storm。但是如果是對實時性要求一般,允許1秒的準實時處理,而且不要求動態調整並行度的話,選擇Spark Streaming是更好的選擇。


20、Spark 機器學習和 Spark 圖計算接觸過沒有,舉例說明你用它做過什麼?

Spark 提供了很多機器學習庫,我們只需要填入數據,設置參數就可以用了。使用起來非常方便。另外一方面,由於它把所有的東西都寫到了內部,我們無法修改其實現過程。要想修改裏面的某個環節,還的修改源碼,重新編譯。比如 kmeans 算法,如果沒有特殊需求,很方便。但是spark內部使用的兩個向量間的距離是歐式距離。如果你想改爲餘弦或者馬氏距離,就的重新編譯源碼了。Spark 裏面的機器學習庫都是一些經典的算法,這些代碼網上也好找。這些代碼使用起來叫麻煩,但是很靈活。Spark 有一個很大的優勢,那就是 RDD。模型的訓練完全是並行的。

Spark 的 ML 和 MLLib 兩個包區別和聯繫

  1. 技術角度上,面向的數據集類型不一樣:ML 的 API 是面向 Dataset 的(Dataframe 是 Dataset 的子集,也就是 Dataset[Row]), mllib 是面對 RDD 的。Dataset 和 RDD 有啥不一樣呢?Dataset 的底端是 RDD。Dataset 對 RDD 進行了更深一層的優化,比如說有 sql 語言類似的黑魔法,Dataset 支持靜態類型分析所以在 compile time 就能報錯,各種 combinators(map,foreach 等)性能會更好,等等。

  2. 編程過程上,構建機器學習算法的過程不一樣:ML 提倡使用 pipelines,把數據想成水,水從管道的一段流入,從另一端流出。ML 是1.4比 Mllib 更高抽象的庫,它解決如果簡潔的設計一個機器學習工作流的問題,而不是具體的某種機器學習算法。未來這兩個庫會並行發展。

21、Spark RDD是怎麼容錯的,基本原理是什麼?

一般來說,分佈式數據集的容錯性有兩種方式:數據檢查點和記錄數據的更新。 

面向大規模數據分析,數據檢查點操作成本很高,需要通過數據中心的網絡連接在機器之間複製龐大的數據集,而網絡帶寬往往比內存帶寬低得多,同時還需要消耗更多的存儲資源。 

因此,Spark選擇記錄更新的方式。但是,如果更新粒度太細太多,那麼記錄更新成本也不低。因此,RDD只支持粗粒度轉換,即只記錄單個塊上執行的單個操作,然後將創建RDD的一系列變換序列(每個RDD都包含了他是如何由其他RDD變換過來的以及如何重建某一塊數據的信息。因此RDD的容錯機制又稱“血統(Lineage)”容錯)記錄下來,以便恢復丟失的分區。 

Lineage本質上很類似於數據庫中的重做日誌(Redo Log),只不過這個重做日誌粒度很大,是對全局數據做同樣的重做進而恢復數據。

22、爲什麼要用Yarn來部署Spark?

因爲 Yarn 支持動態資源配置。Standalone 模式只支持簡單的固定資源分配策略,每個任務固定數量的 core,各 Job 按順序依次分配在資源,資源不夠的時候就排隊。這種模式比較適合單用戶的情況,多用戶的情境下,會有可能有些用戶的任務得不到資源。

Yarn 作爲通用的種子資源調度平臺,除了 Spark 提供調度服務之外,還可以爲其他系統提供調度,如 Hadoop MapReduce, Hive 等。

23、說說yarn-cluster和yarn-client的異同點。

  • cluster 模式會在集羣的某個節點上爲 Spark 程序啓動一個稱爲 Master 的進程,然後 Driver 程序會運行正在這個 Master 進程內部,由這種進程來啓動 Driver 程序,客戶端完成提交的步驟後就可以退出,不需要等待 Spark 程序運行結束,這是四一職中適合生產環境的運行方式

  • client 模式也有一個 Master 進程,但是 Driver 程序不會運行在這個 Master 進程內部,而是運行在本地,只是通過 Master 來申請資源,直到運行結束,這種模式非常適合需要交互的計算。顯然 Driver 在 client 模式下會對本地資源造成一定的壓力。

24、解釋一下 groupByKey, reduceByKey 還有 reduceByKeyLocally

Spark RDD 算子

25、說說 persist() 和 cache() 的異同

RDD的cache和persist的區別

cache()是persist()的簡化方式,調用persist的無參版本,也就是調用persist(StorageLevel.MEMORY_ONLY),cache只有一個默認的緩存級別MEMORY_ONLY,即將數據持久化到內存中,而persist可以通過傳遞一個 StorageLevel 對象來設置緩存的存儲級別。

DataFrame的cache和persist的區別

cache()依然調用的persist(),但是persist調用cacheQuery,而cacheQuery的默認存儲級別爲MEMORY_AND_DISK,這點和rdd是不一樣的。

26、可以解釋一下這兩段程序的異同嗎

val counter = 0
val data = Seq(1, 2, 3)
data.foreach(x => counter += x)
println("Counter value: " + counter)
val counter = 0
val data = Seq(1, 2, 3)
var rdd = sc.parallelizze(data)
rdd.foreach(x => counter += x)
println("Counter value: " + counter)

所有在 Driver 程序追蹤的代碼看上去好像在 Driver 上計算,實際上都不在本地,每個 RDD 操作都被轉換成 Job 分發至集羣的執行器 Executor 進程中運行,即便是單機本地運行模式,也是在單獨的執行器進程上運行,與 Driver 進程屬於不用的進程。所以每個 Job 的執行,都會經歷序列化、網絡傳輸、反序列化和運行的過程。

再具體一點解釋是 foreach 中的匿名函數 x => counter += x 首先會被序列化然後被傳入計算節點,反序列化之後再運行,因爲 foreach 是 Action 操作,結果會返回到 Driver 進程中。

在序列化的時候,Spark 會將 Job 運行所依賴的變量、方法全部打包在一起序列化,相當於它們的副本,所以 counter 會一起被序列化,然後傳輸到計算節點,是計算節點上的 counter 會自增,而 Driver 程序追蹤的 counter 則不會發生變化。執行完成之後,結果會返回到 Driver 程序中。而 Driver 中的 counter 依然是當初的那個 Driver 的值爲0。

因此說,RDD 操作不能嵌套調用,即在 RDD 操作傳入的函數參數的函數體中,不可以出現 RDD 調用。

27、說說map和mapPartitions的區別

map 中的 func 作用的是 RDD 中每一個元素,而 mapPartitioons 中的 func 作用的對象是 RDD 的一整個分區。所以 func 的類型是 Iterator<T> => Iterator<T>,其中 T 是輸入 RDD 的元素類型。

28、groupByKey和reduceByKey是屬於Transformation還是 Action?

前者,因爲 Action 輸出的不再是 RDD 了,也就意味着輸出不是分佈式的,而是回送到 Driver 程序。以上兩種操作都是返回 RDD,所以應該屬於 Transformation。

29、說說Spark支持的3種集羣管理器

Standalone 模式:資源管理器是 Master 節點,調度策略相對單一,只支持先進先出模式。

Hadoop Yarn 模式:資源管理器是 Yarn 集羣,主要用來管理資源。Yarn 支持動態資源的管理,還可以調度其他實現了 Yarn 調度接口的集羣計算,非常適用於多個集羣同時部署的場景,是目前最流行的一種資源管理系統。

Apache Mesos:Mesos 是專門用於分佈式系統資源管理的開源系統,與 Yarn 一樣是 C++ 開發,可以對集羣中的資源做彈性管理。

30、說說Worker和Excutor的異同

Worker 是指每個及節點上啓動的一個進程,負責管理本節點,jps 可以看到 Worker 進程在運行。Excutor 每個Spark 程序在每個節點上啓動的一個進程,專屬於一個 Spark 程序,與 Spark 程序有相同的生命週期,負責 Spark 在節點上啓動的 Task,管理內存和磁盤。如果一個節點上有多個 Spark 程序,那麼相應就會啓動多個執行器。

31、說說Spark提供的兩種共享變量

Spark 程序的大部分操作都是 RDD 操作,通過傳入函數給 RDD 操作函數來計算,這些函數在不同的節點上併發執行,內部的變量有不同的作用域,不能相互訪問,有些情況下不太方便。

  1. 廣播變量,是一個只讀對象,在所有節點上都有一份緩存,創建方法是 SparkContext.broadcast()。創建之後再更新它的值是沒有意義的,一般用 val 來修改定義。

  2. 計數器,只能增加,可以用計數或求和,支持自定義類型。創建方法是 SparkContext.accumulator(V, name)。只有 Driver 程序可以讀這個計算器的變量,RDD 操作中讀取計數器變量是無意義的。

以上兩種類型都是 Spark 的共享變量。

32、說說檢查點的意義

在容錯機制中,如果一個節點死機了,而且運算窄依賴,則只要把丟失的父RDD分區重算即可,不依賴於其他節點。而寬依賴需要父RDD的所有分區都存在,重算就很昂貴了。可以這樣理解開銷的經濟與否:在窄依賴中,在子RDD的分區丟失、重算父RDD分區時,父RDD相應分區的所有數據都是子RDD分區的數據,並不存在冗餘計算。在寬依賴情況下,丟失一個子RDD分區重算的每個父RDD的每個分區的所有數據並不是都給丟失的子RDD分區用的,會有一部分數據相當於對應的是未丟失的子RDD分區中需要的數據,這樣就會產生冗餘計算開銷,這也是寬依賴開銷更大的原因。因此如果使用Checkpoint算子來做檢查點,不僅要考慮Lineage是否足夠長,也要考慮是否有寬依賴,對寬依賴加Checkpoint是最物有所值的。

33、說說Spark的高可用和容錯

Spark 應用程序的高可用性主要包含兩個部分:集羣環境的高可用以及應用程序的容錯特性;集羣環境的高可用,主要由集羣框架來控制,比如 spark on yarn 模式下的 ResourceManager 的 HA、Spark Standalone 模式下的 Master HA 等特性的設置保障集羣的高可用性;至於應用程序的容錯需要考慮應用的各個組成部分的容錯。

spark 應用程序執行過程中,一般存在以下失敗的情況:

  • Driver 集成宕機:Driver 運行機器宕機、Driver 程序運行過程中異常導致宕機
  • Executor 進程宕機:Executor 所在的work 宕機,Exector 和 Driver 通信超時
  • Task 執行失敗:task 執行過程發生異常導致失敗

Driver 進程宕機解決方案:

  • 監控機器機器是否存活,如果機器宕機,重啓服務機器和 spark 集羣
  • 通過 spark job 的 history 服務監控應用是否執行成功,如果執行失敗,通過開發人員重啓服務即可
  • SparkStreaming 中,重啓spark應用後,可通過 checkpoint 進行job數據恢復

Executor 宕機解決方案:選擇一個work 節點重啓Executor 進程,Driver 重新分配任務

Task 執行失敗解決方案:

  • Spark 會自動進行 task 重試機制,如果某個 task 失敗重試次數超過3次(spark.task.maxFailures)後,當前job 執行失敗;local 模式默認不啓用 task 重試機制
  • Task 數據恢復/重新運行的機制實際上是 RDD 容錯機制,即 Lineage 機制,RDD的 Lineage 機制記錄的是粗粒度的特定數據的 Transformation 操作行爲。當這個 RDD 的部分數據丟失時,它可以通過 lineage 獲取足夠的信息來重新運算和恢復丟失的數據分區;該機制體現在RDD上就是RDD依賴特性
  • 如果 rdd 的 lineage 的生命線特別長,此時某些 task 執行失敗的恢復成本就會比較高,那麼可以採用檢查點或緩存的方式將數據冗餘下來,當檢查點/緩存點之後的rdd的task出現異常的時候,可以直接從檢查點重新構建lineage,可以減少執行開銷。

34、解釋一下Spark Master的選舉過程

Master作爲Spark standalone模式的核心,如果Master出現異常,那麼集羣就不能正常工作。所以Spark會從Standby中選擇一個節點作爲Master.

 Spark支持以下幾種策略,這種策略可以通過配置文件spark-env.sh配置spark.deploy.recoveryMode

  • ZOOKEEPER: 集羣元數據持久化到zookeeper,當master出現異常的時候,zookeeper會通過選舉機制選舉出新的Master,新的Master接管集羣時需要從zookeeper獲取持久化信息,並根據這些信息恢復集羣狀態
  • FILESYSTEM: 集羣的元數據持久化到文件系統,當Master出現異常的時候,只要在該機器上重啓Master,啓動後的Master獲取持久化信息並根據持久化信息恢復集羣狀態
  • CUSTOM: 自定義恢復模式,實現StandaloneRecoveryModeFactory抽象類進行實現,並把該類配置到配置文件,當Master出現異常,會根據用戶自定義的方式進行恢復集羣狀況
  • NONE: 不持久化集羣元數據,當Master出現異常時,新啓動的Master不進行恢復集羣狀態

35、說說Spark如何實現序列化組件的

Spark通過兩種方式來創建序列化器

Java序列化

在默認情況下,Spark 採用 Java的 ObjectOutputStream 序列化一個對象。該方式適用於所有實現了 java.io.Serializable 的類。通過繼承 java.io.Externalizable,你能進一步控制序列化的性能。Java序列化非常靈活,但是速度較慢,在某些情況下序列化的結果也比較大。

Kryo序列化

Spark 也能使用 Kryo(版本2)序列化對象。Kryo 不但速度極快,而且產生的結果更爲緊湊(通常能提高10倍)。Kryo 的缺點是不支持所有類型,爲了更好的性能,你需要提前註冊程序中所使用的類(class)。

Java 的序列化比較簡單,就和前面的一樣,下面主要介紹Kryo序列化的使用。

Kryo序列化怎麼用?

可以在創建 SparkContext 之前,通過調用 System.setProperty("spark.serializer", "spark.KryoSerializer"),將序列化方式切換成Kryo。

但是 Kryo 需要用戶進行註冊,這也是爲什麼 Kryo 不能成爲 Spark 序列化默認方式的唯一原因,但是建議對於任何“網絡密集型”(network-intensive)的應用,都採用這種方式進行序列化方式。

Kryo文檔描述了很多便於註冊的高級選項,例如添加用戶自定義的序列化代碼。

如果對象非常大,你還需要增加屬性 spark.kryoserializer.buffer.mb 的值。該屬性的默認值是32,但是該屬性需要足夠大以便能夠容納需要序列化的最大對象。

最後,如果你不註冊你的類,Kryo仍然可以工作,但是需要爲了每一個對象保存其對應的全類名(full class name),這是非常浪費的。

package com.zhangpengfei.spark_demo.kyro

import com.esotericsoftware.kryo.Kryo
import org.apache.spark.SparkContext
import org.apache.spark.serializer.KryoRegistrator


class KryoDemo1 {}

class KyroClass extends KryoRegistrator {
  override def registerClasses(kryo: Kryo): Unit = {
    kryo.register(classOf[KryoDemo1])
  }
}
object KryoClass {
  def main(args: Array[String]): Unit = {
    System.setProperty("spark.serializer", "spark.KryoSerializer")
    System.setProperty("spark.kryo.registrator", "com.zhangpengfei.spark_demo.kyro.KyroClass")
    val context = new SparkContext()
  }
}

36、說說對Master的理解

Master 是 local-cluster 部署模式和 Standalone 部署模式中,整個 Spark 集羣最爲重要的組件之一,分擔了對整個集羣資源的管理和分配的工作。

local-cluser 下,Master 作爲 JVM 進程的對象啓動,而在 Standalone 模式下,就是單獨的進程啓動。 

37、說說什麼是窗口間隔和滑動間隔

也叫 WriteAheadLogs,通常被用於數據庫和文件系統中,保證數據操作的持久性。預寫日誌通常是先將操作寫入到一個持久可靠的日誌文件中,然後纔對數據施加該操作,當加入施加該操作中出現異常,可以通過讀取日誌文件並重新施加該操作,從而恢復系統。

當 WAL 開啓後,所有收到的數據同時保存到了容錯文件系統的日誌文件中,當 Spark Streaming 失敗,這些接受到的數據也不會丟失。另外,接收數據的正確性只在數據被預寫到日誌以後接收器纔會確認。已經緩存但還沒有保存的數據可以在 Driver 重新啓動之後由數據源再發送一次(經常問)。

這兩個機制保證了數據的零丟失,即所有的數據要麼從日誌中恢復,要麼由數據源重發。

38、Spark Streaming小文件問題

使用 Spark Streaming 時,如果實時計算結果要寫入到 HDFS,那麼不可避免的會遇到一個問題,那就是在默認情況下會產生非常多的小文件,這是由 Spark Streaming 的微批處理模式和 DStream(RDD) 的分佈式(partition)特性導致的,Spark Streaming 爲每個 Partition 啓動一個獨立的線程(一個 task/partition 一個線程)來處理數據,一旦文件輸出到 HDFS,那麼這個文件流就關閉了,再來一個 batch 的 parttition 任務,就再使用一個新的文件流,那麼假設,一個 batch 爲10s,每個輸出的 DStream 有32個 partition,那麼一個小時產生的文件數將會達到(3600/10)*32=11520個之多。衆多小文件帶來的結果是有大量的文件元信息,比如文件的 location、文件大小、block number 等需要 NameNode 來維護,NameNode 會因此鴨梨山大。不管是什麼格式的文件,parquet、text、JSON 或者 Avro,都會遇到這種小文件問題,這裏討論幾種處理 Spark Streaming 小文件的典型方法。

  1. 增加 batch 大小:這種方法很容易理解,batch 越大,從外部接收的 event 就越多,內存積累的數據也就越多,那麼輸出的文件數也就會變少,比如上邊的時間從10s增加爲100s,那麼一個小時的文件數量就會減少到1152個。但別高興太早,實時業務能等那麼久嗎,本來人家10s看到結果更新一次,現在要等快兩分鐘,是人都會罵娘。所以這種方法適用的場景是消息實時到達,但不想擠壓在一起處理,因爲擠壓在一起處理的話,批處理任務在乾等,這時就可以採用這種方法。

  2. Coalesce大法好:文章開頭講了,小文件的基數是 batch_number * partition_number,而第一種方法是減少 batch_number,那麼這種方法就是減少 partition_number 了,這個 api 不細說,就是減少初始的分區個數。看過 spark 源碼的童鞋都知道,對於窄依賴,一個子 RDD 的 partition 規則繼承父 RDD,對於寬依賴(就是那些個xxxByKey操作),如果沒有特殊指定分區個數,也繼承自父 rdd。那麼初始的 SourceDstream 是幾個 partiion,最終的輸出就是幾個 partition。所以 Coalesce 大法的好處就是,可以在最終要輸出的時候,來減少一把 partition 個數。但是這個方法的缺點也很明顯,本來是32個線程在寫256M數據,現在可能變成了4個線程在寫256M數據,而沒有寫完成這256M數據,這個 batch 是不算結束的。那麼一個 batch 的處理時延必定增長,batch 擠壓會逐漸增大。

  3. Spark Streaming 外部來處理:我們既然把數據輸出到 hdfs,那麼說明肯定是要用 Hive 或者 Spark Sql 這樣的“sql on hadoop”系統類進一步進行數據分析,而這些表一般都是按照半小時或者一小時、一天,這樣來分區的(注意不要和 Spark Streaming 的分區混淆,這裏的分區,是用來做分區裁剪優化的),那麼我們可以考慮在 Spark Streaming 外再啓動定時的批處理任務來合併 Spark Streaming 產生的小文件。這種方法不是很直接,但是卻比較有用,“性價比”較高,唯一要注意的是,批處理的合併任務在時間切割上要把握好,搞不好就可能會去合併一個還在寫入的 Spark Streaming 小文件。

  4. 自己調用 foreach 去 append:Spark Streaming 提供的 foreach 這個 outout 類 api (一種 Action 操作),可以讓我們自定義輸出計算結果的方法。那麼我們其實也可以利用這個特性,那就是每個 batch 在要寫文件時,並不是去生成一個新的文件流,而是把之前的文件打開。考慮這種方法的可行性,首先,HDFS 上的文件不支持修改,但是很多都支持追加,那麼每個 batch 的每個 partition 就對應一個輸出文件,每次都去追加這個 partition 對應的輸出文件,這樣也可以實現減少文件數量的目的。這種方法要注意的就是不能無限制的追加,當判斷一個文件已經達到某一個閾值時,就要產生一個新的文件進行追加了。所以大概就是一直32個文件。

39、Spark的UDF?

因爲目前 Spark SQL 本身支持的函數有限,一些常用的函數都沒有,比如 len, concat...etc 但是使用 UDF 來自己實現根據業務需要的功能是非常方便的。Spark SQL UDF 其實是一個 Scala 函數,被 catalyst 封裝成一個 Expression 結點,最後通過 eval 方法計根據當前 Row 計算 UDF 的結果。UDF 對錶中的單行進行轉換,以便爲每行生成單個對應的輸出值。例如,大多數 SQL 環境提供 UPPER 函數返回作爲輸入提供的字符串的大寫版本。

用戶自定義函數可以在 Spark SQL 中定義和註冊爲 UDF,並且可以關聯別名,這個別名可以在後面的 SQL 查詢中使用。作爲一個簡單的示例,我們將定義一個 UDF 來將以下 JSON 數據中的溫度從攝氏度(degrees Celsius)轉換爲華氏度(degrees Fahrenheit)。

{"city":"St. John's","avgHigh":8.7,"avgLow":0.6}
{"city":"Charlottetown","avgHigh":9.7,"avgLow":0.9}
{"city":"Halifax","avgHigh":11.0,"avgLow":1.6}
{"city":"Fredericton","avgHigh":11.2,"avgLow":-0.5}
{"city":"Quebec","avgHigh":9.0,"avgLow":-1.0}
{"city":"Montreal","avgHigh":11.1,"avgLow":1.4}
...

以下示例代碼使用 SQL 別名爲 CTOF 來註冊我們的轉換 UDF,然後在 SQL 查詢使用它來轉換每個城市的溫度。爲簡潔起見,省略了 SQLContext 對象和其他代碼的創建,每段代碼下面都提供了完整的代碼鏈接。

# Python
df = sqlContext.read.json("temperatures.json")

df.registerTempTable("citytemps")
# Register the UDF with our SQLContext

sqlContext.registerFunction("CTOF", lambda degreesCelsius: ((degreesCelsius * 9.0 / 5.0) + 32.0))
sqlContext.sql("SELECT city, CTOF(avgLow) AS avgLowF, CTOF(avgHigh) AS avgHighF FROM citytemps").show()
# Scala
val df = sqlContext.read.json("temperatures.json")

df.registerTempTable("citytemps")
// Register the UDF with our SQLContext

sqlContext.udf.register("CTOF", (degreesCelcius: Double) => ((degreesCelcius * 9.0 / 5.0) + 32.0))
sqlContext.sql("SELECT city, CTOF(avgLow) AS avgLowF, CTOF(avgHigh) AS avgHighF FROM citytemps").show()
# Java
DataFrame df = sqlContext.read().json("temperatures.json");
df.registerTempTable("citytemps");

// Register the UDF with our SQLContext
sqlContext.udf().register("CTOF", new UDF1<Double, Double>() {
  @Override
  public Double call(Double degreesCelcius) {
    return ((degreesCelcius * 9.0 / 5.0) + 32.0);
  }
}, DataTypes.DoubleType);
sqlContext.sql("SELECT city, CTOF(avgLow) AS avgLowF, CTOF(avgHigh) AS avgHighF FROM citytemps").show();

40、Mesos下粗粒度和細粒度對比?

  1. 粗粒度運行模式:Spark 應用程序在註冊到 Mesos 時會分配對應系統資源,在執行過程中由 SparkContext 和 Executor 直接交互,該模式優點是由於資源長期持有減少了資源調度的時間開銷,缺點是該模式下 Mesos 無法感知資源使用的變化,容易造成系統資源的閒置,無法被 Mesos 其他框架使用,造成資源浪費。
  2. 細粒度的運行模式:Spark 應用程序是以單個任務的粒度發送到 Mesos 中執行,在執行過程中 SparkContext 並不能和 Executor 直接交互,而是由 Mesos Master 進行統一的調度管理,這樣能夠根據整個 Mesos 集羣資源使用的情況動態調整。該模式的優點是系統資源能夠得到充分利用,缺點是該模式中每個人物都需要從 Mesos 獲取資源,調度延遲較大,對於 Mesos Master 開銷較大。

41、Spark Local和Standalone有什麼區別

Spark一共有5種運行模式:Local,Standalone,Yarn-Cluster,Yarn-Client 和 Mesos。

  1. Local:Local 模式即單機模式,如果在命令語句中不加任何配置,則默認是 Local 模式,在本地運行。這也是部署、設置最簡單的一種模式,所有的 Spark 進程都運行在一臺機器或一個虛擬機上面。

  2. Standalone:Standalone 是 Spark 自身實現的資源調度框架。如果我們只使用 Spark 進行大數據計算,不使用其他的計算框架(如MapReduce或者Storm)時,就採用 Standalone 模式就夠了,尤其是單用戶的情況下。Standalone 模式是 Spark 實現的資源調度框架,其主要的節點有 Client 節點、Master 節點和 Worker 節點。其中 Driver 既可以運行在 Master 節點上中,也可以運行在本地 Client 端。當用 spark-shell 交互式工具提交 Spark 的 Job 時,Driver 在 Master 節點上運行;當使用 spark-submit 工具提交 Job 或者在 Eclipse、IDEA 等開發平臺上使用 new SparkConf.setManager(“spark://master:7077”) 方式運行 Spark 任務時,Driver 是運行在本地 Client 端上的。

Standalone 模式的部署比較繁瑣,需要把 Spark 的部署包安裝到每一臺節點機器上,並且部署的目錄也必須相同,而且需要 Master 節點和其他節點實現 SSH 無密碼登錄。啓動時,需要先啓動 Spark 的 Master 和 Slave 節點。提交命令類似於:

./bin/spark-submit \
  --class org.apache.spark.examples.SparkPi \
  --master spark://Oscar-2.local:7077 \
  /tmp/spark-2.2.0-bin-hadoop2.7/examples/jars/spark-examples_2.11-2.2.0.jar \
  100

其中 master:7077是 Spark 的 Master 節點的主機名和端口號。當然集羣是需要提前啓動。

42、說說SparkContext和SparkSession有什麼區別?

  1. Application: 用戶編寫的 Spark 應用程序,Driver 即運行上述 Application 的 main() 函數並且創建 SparkContext。Application 也叫應用。

  2. SparkContext: 整個應用的上下文,控制應用的生命週期。

  3. RDD: 不可變的數據集合,可由 SparkContext 創建,是 Spark 的基本計算單元。

  4. SparkSession: 可以由上節圖中看出,Application、SparkSession、SparkContext、RDD之間具有包含關係,並且前三者是1對1的關係。SparkSession 是 Spark 2.0 版本引入的新入口,在這之前,創建一個 Application 對應的上下文是這樣的:

//set up the spark configuration and create contexts
val sparkConf = new SparkConf().setAppName("SparkSessionZipsExample").setMaster("local")
// your handle to SparkContext to access other context like SQLContext
val sc = new SparkContext(sparkConf).set("spark.some.config.option", "some-value")
val sqlContext = new org.apache.spark.sql.SQLContext(sc)

現在 SparkConf、SparkContext 和 SQLContext 都已經被封裝在 SparkSession 當中,並且可以通過 builder 的方式創建:

// Create a SparkSession. No need to create SparkContext
// You automatically get it as part of the SparkSession
val warehouseLocation = "file:${system:user.dir}/spark-warehouse"
val spark = SparkSession
   .builder()
   .appName("SparkSessionZipsExample")
   .config("spark.sql.warehouse.dir", warehouseLocation)
   .enableHiveSupport()
   .getOrCreate()

通過 SparkSession 創建並操作 Dataset 和 DataFrame,代碼中的 spark 對象就是 SparkSession:

//create a Dataset using spark.range starting from 5 to 100, with increments of 5
val numDS = spark.range(5, 100, 5)
// reverse the order and display first 5 items
numDS.orderBy(desc("id")).show(5)
//compute descriptive stats and display them
numDs.describe().show()
// create a DataFrame using spark.createDataFrame from a List or Seq
val langPercentDF = spark.createDataFrame(List(("Scala", 35), ("Python", 30), ("R", 15), ("Java", 20)))
//rename the columns
val lpDF = langPercentDF.withColumnRenamed("_1", "language").withColumnRenamed("_2", "percent")
//order the DataFrame in descending order of percentage
lpDF.orderBy(desc("percent")).show(false)

43、如果Spark Streaming停掉了,如何保證Kafka的重新運作是合理的呢

首先要說一下 Spark 的快速故障恢復機制,在節點出現故障的情況下,傳統流處理系統會在其他節點上重啓失敗的連續算子,並可能沖洗能運行先前數據流處理操作獲取部分丟失數據。在此過程中只有該節點重新處理失敗的過程。只有在新節點完成故障前所有計算後,整個系統才能夠處理其他任務。在 Spark 中,計算將會分成許多小的任務,保證能在任何節點運行後能夠正確合併,因此,就算某個節點出現故障,這個節點的任務將均勻地分散到集羣中的節點進行計算,相對於傳遞故障恢復機制能夠更快地恢復。

44、列舉Spark中 Transformation 和 Action算子

Transformantion:Map, Filter, FlatMap, Sample, GroupByKey, ReduceByKey, Union, Join, Cogroup, MapValues, Sort, PartionBy

Action:Collect, Reduce, Lookup, Save (主要記住,結果不是 RDD 的就是 Action)

45、Spark經常說的Repartition是個什麼玩意

簡單的說:返回一個恰好有numPartitions個分區的RDD,可以增加或者減少此RDD的並行度。內部,這將使用shuffle重新分佈數據,如果你減少分區數,考慮使用coalesce,這樣可以避免執行shuffle。目的:

  • 避免小文件

  • 減少 Task 個數

  • 但是會增加每個 Task 處理的數據量

46、Spark Streaming Duration的概念

Spark Streaming 是微批處理。

 SparkConf sparkConf = new SparkConf().setAppName("SparkStreaming").setMaster("local[*]"); 
 JavaStreamingContext javaStreamingContext = new JavaStreamingContext(sparkConf, Durations.seconds(1000));

Durations.seconds(1000)設置的是 sparkstreaming 批處理的時間間隔,每隔 Batch Duration 時間去提交一次 job,如果 job 的處理時間超過 Batch Duration,會使得 job 無法按時提交,隨着時間推移,越來越多的作業被拖延,最後導致整個 Streaming 作業被阻塞,無法做到實時處理數據。

47、簡單寫一個WordCount程序

sc.textFile("/Users/runzhliu/workspace/spark-2.2.1-bin-hadoop2.7/README.md")
.flatMap(_.split(" "))
.map(x => (x, 1))
.reduceByKey(_ + _)
.map(x => (x._2, x._1))
.sortByKey(false)
.map(x => (x._2, x._1))
.take(10)

48、說說Yarn-cluster的運行階段

在 Yarn-cluset 模式下,當用戶向 Yarn 提交一個應用程序後,Yarn 將兩個階段運行該應用程序:

  1. 第一階段是把 Spark 的 Driver 作爲一個 Application Master 在 Yarn 集羣中先啓動。

  2. 第二階段是由 Application Master 創建應用程序,然後爲它向 Resource Manager 申請資源,並啓動 Executor 來運行任務集,同時監控它的整個過程,直到運行介紹結束。

49、Mesos粗細度對比

Mesos 粗粒度運行模式中,Spark 程序在註冊到 Mesos 的時候會分配對應系統資源,在執行過程中由 SparkContext 和 Executor 直接進行交互。該模式優點是由於資源長期持有,減少了資源調度的時間開銷,缺點是該模式之下,Mesos 無法感知資源使用的變化,容易造成資源的閒置,無法被 Mesos 其他框架所使用,從而造成資源浪費。

而在細粒度運行模式下,Spark 應用程序是以單個任務的粒度發送到 Mesos 中執行,在執行過程中 SparkContext 並不能與 Executor 直接進行交互,而是由 Mesos Master 進行統一的調度管理,這樣能夠根據整個 Mesos 集羣資源使用的情況動態調整。該模式的優點是系統資源能夠得到充分利用,缺點是該模式中每個任務都需要從 Mesos 獲取資源,調度延遲比較大,對於 Mesos 開銷比較大。

50、說說Standalone模式下運行Spark程序的大概流程

Standalone 模式分別由客戶端、Master 節點和 Worker 節點組成。在 Spark Shell 提交計算代碼的時候,所在機器作爲客戶端啓動應用程序,然後向 Master 註冊應用程序,由 Master 通知 Worker 節點啓動 Executor,Executor 啓動之後向客戶端的 Driver 註冊,最後由 Driver 發送執行任務給 Executor 並監控任務執行情況。該程序代碼中,在觸發計算行數動作之前,需要設置緩存代碼,這樣在執行計算行數行爲的時候進行緩存數據,緩存後再運行計算行數。

51、如何區分 Appliction(應用程序)還有 Driver(驅動程序)

Application 是指用戶編寫的 Spark 應用程序,包含驅動程序 Driver 和分佈在集羣中多個節點上運行的 Executor 代碼,在執行過程之中由一個或多個做作業組成。

Driver 是 Spark 中的 Driver 即運行上述 Application 的 main 函數並且創建 SparkContext,其中創建 SparkContext 的目的是爲了準備 Spark 應用程序的運行環境。在 Spark 中由 sc 負責與 ClusterManager 通信,進行資源的申請,任務的分配和監控等。當 Executor 部分運行完畢後,Driver 負責把 sc 關閉,通常 Driver 會拿 SparkContext 來代表。

52、介紹一下 Spark 通信的啓動方式

Spark 啓動過程主要是 Master 與 Worker 之間的通信,首先由 Worker 節點向 Master 發送註冊消息,然後 Master 處理完畢後,返回註冊成功消息或失敗消息,如果成功註冊,那麼 Worker 就會定時發送心跳消息給 Master。

53、介紹一下 Spark 運行時候的消息通信

用戶提交應用程序時,應用程序的 SparkContext 會向 Master 發送應用註冊消息,並由 Master 給該應用分配 Executor,Excecutor 啓動之後,Executor 會向 SparkContext 發送註冊成功消息。當 SparkContext 的 RDD 觸發行動操作之後,將創建 RDD 的 DAG。通過 DAGScheduler 進行劃分 Stage 並把 Stage 轉化爲 TaskSet,接着 TaskScheduler 向註冊的 Executor 發送執行消息,Executor 接收到任務消息後啓動並運行。最後當所有任務運行時候,由 Driver 處理結果並回收資源。

54、解釋一下Stage

每個作業會因爲 RDD 之間的依賴關係拆分成多組任務集合,稱爲調度階段,也叫做任務集。調度階段的劃分由 DAGScheduler 劃分,調度階段有 Shuffle Map Stage 和 Result Stage 兩種。

55、描述一下Worker異常的情況

Spark 獨立運行模式 Standalone 採用的是 Master/Slave 的結構,其中 Slave 是由 Worker 來擔任的,在運行的時候會發送心跳給 Master,讓 Master 知道 Worker 的實時狀態,另一方面,Master 也會檢測註冊的 Worker 是否超時,因爲在集羣運行的過程中,可能由於機器宕機或者進程被殺死等原因造成 Worker 異常退出。

56、描述一下Master異常的情況

Master 出現異常的時候,會有幾種情況,而在獨立運行模式 Standalone 中,Spark 支持幾種策略,來讓 Standby Master 來接管集羣。主要配置的地方在於 spark-env.sh 文件中。配置項是 spark.deploy.recoveryMode 進行設置,默認是 None。

  1. ZOOKEEPER:集羣元數據持久化到 Zookeeper 中,當 Master 出現異常,ZK 通過選舉機制選舉新的 Master,新的 Master 接管的時候只要從 ZK 獲取持久化信息並根據這些信息恢復集羣狀態。StandBy 的 Master 隨時候命的。

  2. FILESYSTEM:集羣元數據持久化到本地文件系統中,當 Master 出現異常的時候,只要在該機器上重新啓動 Master,啓動後新的 Master 獲取持久化信息並根據這些信息恢復集羣的狀態。

  3. CUSTOM:自定義恢復方式,對 StandaloneRecoveryModeFactory 抽象類進行實現並把該類配置到系統中,當 Master 出現異常的時候,會根據用戶自定義的方式進行恢復集羣狀態。

  4. NONE:不持久化集羣的元數據,當出現異常的是,新啓動 Master 不進行信息恢復集羣狀態,而是直接接管集羣。

57、Spark的存儲體系

簡單來講,Spark存儲體系是各個Driver與Executor實例中的BlockManager所組成的;但是從一個整體來看,把各個節點的BlockManager看成存儲體系的一部分,那存儲體系就有了更多衍生的內容,比如塊傳輸服務、map任務輸出跟蹤器、Shuffle管理器等。


58、簡述Spark Streaming

具有高吞吐量和容錯能力強的特點,輸入源有很多,如 Kafka, Flume, Twitter 等待。

關於流式計算的做法,如果按照傳統工具的做法把數據存儲到數據庫中再進行計算,這樣是無法做到實時的,而完全把數據放到內存中計算,萬一宕機、斷電了,數據也就丟失了。

因此 Spark 流式計算引入了檢查點 CheckPoint 和日誌,以便能夠從中恢復計算結果。而本質上 Spark Streaming 是接收實時輸入數據流並把他們按批次劃分,然後交給 Spark 計算引擎處理生成按照批次劃分的結果流。

59、知道 Hadoop MRv1 的侷限嗎

  1. 可擴展性查,在運行的時候,JobTracker 既負責資源管理,又負責任務調度,當集羣繁忙的時候,JobTracker 很容易成爲瓶頸,最終導致可擴展性的問題。

  2. 可用性差,採用單節點的 Master 沒有備用 Master 以及選舉操作,這導致一旦 Master 出現故障,整個集羣將不可用。

  3. 資源利用率低,TaskTracker 使用 slot 等量劃分本節點上的資源量,slot 代表計算資源將各個 TaskTracker 上的空閒 slot 分配給 Task 使用,一些 Task 並不能充分利用 slot,而其他 Task 無法使用這些空閒的資源。有時會因爲作業剛剛啓動等原因導致 MapTask 很多,而 Reduce Task 任務還沒調度的情況,這時 Reduce slot 也會被閒置。

  4. 不能支持多種 MapReduce 框架,無法通過可插拔方式將自身的 MapReduce 框架替換爲其他實現,例如 Spark,Storm。

60、說說Spark的特點,相對於MR來說

  1. 減少磁盤 I/O,MR 會把 map 端將中間輸出和結果存儲在磁盤中,reduce 端又需要從磁盤讀寫中間結果,勢必造成磁盤 I/O 稱爲瓶頸。Spark 允許將 map 端的中間結果輸出和結果存儲在內存中,reduce 端在拉取中間結果的時候避免了大量的磁盤 I/O。

  2. 增加並行度,由於把中間結果寫到磁盤與從磁盤讀取中間結果屬於不同的緩解,Hadoop 將他們簡單地通過串行執行銜接起來,Spark 則把不同的環節抽象成爲 Stage,允許多個 Stage 既可以串行又可以並行執行。

  3. 避免重新計算,當 Stage 中某個分區的 Task 執行失敗後,會重新對此 Stage 調度,但在重新調度的時候會過濾已經執行成功的分區任務,所以不會造成重複計算和資源浪費。

  4. 可選的 Shuffle 排序,MR 在 Shuffle 之前有着固定的排序操作,而 Spark 則可以根據不同場景選擇在 map 端排序還是 reduce 排序。

  5. 靈活的內存管理策略,Spark 將內存分爲堆上的存儲內存、堆外的存儲內存,堆上的執行內存,堆外的執行內存4個部分。

61、說說Spark Narrow Dependency的分類

  • OneToOneDependency

  • RangeDependency

62、Task和Stage的分類

Task 指具體的執行任務,一個 Job 在每個 Stage 內都會按照 RDD 的 Partition 數量,創建多個 Task,Task 分爲 ShuffleMapTask 和 ResultTask 兩種。ShuffleMapStage 中的 Task 爲 ShuffleMapTask,而 ResultStage 中的 Task 爲 ResultTask。ShuffleMapTask 和 ResultTask 類似於 Hadoop 中的 Map 任務和 Reduce 任務。

63、Spark的編程模型

1.創建應用程序 SparkContext

2.創建RDD,有兩種方式,方式一:輸入算子,即讀取外部存儲創建RDD,Spark與Hadoop完全兼容,所以對Hadoop所支持的文件類型或者數據庫類型,Spark同樣支持。方式二:從集合創建RDD

3.Transformation 算子,這種變換並不觸發提交作業,完成作業中間過程處理。也就是說從一個RDD 轉換生成另一個 RDD 的轉換操作不是馬上執行,需要等到有 Action 操作的時候纔會真正觸發運算。

4.Action 算子,這類算子會觸發 SparkContext 提交 Job 作業。並將數據輸出 Spark系統。

5.保存結果

6.關閉應用程序

64、Spark的計算模型

用戶程序對 RDD 通過多個函數進行操作,將 RDD 進行轉換。

Block-Manager 管理 RDD 的物理分區,每個 Block 就是節點上對應的一個數據塊,可以存儲在內存或者磁盤。

而 RDD 中的 partition 是一個邏輯數據塊,對應相應的物理塊 Block。

本質上一個 RDD 在代碼中相當於是數據的一個元數據結構,存儲着數據分區及其邏輯結構映射關係,存儲着 RDD 之前的依賴轉換關係

65、總述Spark的架構

從集羣部署的角度來看,Spark 集羣由集羣管理器 Cluster Manager、工作節點 Worker、執行器 Executor、驅動器 Driver、應用程序 Application 等部分組成。

Cluster Manager:主要負責對集羣資源的分配和管理,Cluster Manager 在 YARN 部署模式下爲 RM,在 Mesos 下爲 Mesos Master,Standalone 模式下爲 Master。CM 分配的資源屬於一級分配,它將各個 Worker 上的內存、CPU 等資源分配給 Application,但是不負責對 Executor 的資源分類。Standalone 模式下的 Master 會直接給 Application 分配內存、CPU 及 Executor 等資源。

Worker:Spark 的工作節點。在 YARN 部署模式下實際由 NodeManager 替代。Worker 節點主要負責,把自己的內存、CPU 等資源通過註冊機制告知 CM,創建 Executor,把資源和任務進一步分配給 Executor,同步資源信息,Executor 狀態信息給 CM 等等。Standalone 部署模式下,Master 將 Worker 上的內存、CPU 以及 Executor 等資源分配給 Application 後,將命令 Worker 啓動 CoarseGrainedExecutorBackend 進程(此進程會創建 Executor 實例)。

Executor:執行計算任務的一線組件,主要負責任務的執行及與 Worker Driver 信息同步。

Driver:Application 的驅動程序,Application 通過 Driver 與 CM、Executor 進行通信。Driver 可以運行在 Application 中,也可以由 Application 提交給 CM 並由 CM 安排 Worker 運行。

Application:用戶使用 Spark 提供的 API 編寫的應用程序,Application 通過 Spark API 將進行 RDD 的轉換和 DAG 的創建,並通過 Driver 將 Application 註冊到 CM,CM 將會根據 Application 的資源需求,通過一級資源分配將 Excutor、內存、CPU 等資源分配給 Application。Drvier 通過二級資源分配將 Executor 等資源分配給每一個任務,Application 最後通過 Driver 告訴 Executor 運行任務。

66、一句話說說 Spark Streaming 是如何收集和處理數據的

在 Spark Streaming 中,數據採集是逐條進行的,而數據處理是按批 mini batch進行的,因此 Spark Streaming 會先設置好批處理間隔 batch duration,當超過批處理間隔就會把採集到的數據彙總起來成爲一批數據交給系統去處理。

67、解釋一下窗口間隔window duration和滑動間隔slide duration

Spark Streaming

  1. 紅色的矩形就是一個窗口,窗口 hold 的是一段時間內的數據流。

  2. 這裏面每一個 time 都是時間單元,在官方的例子中,每隔 window size 是3 time unit, 而且每隔2個單位時間,窗口會 slide 一次。

所以基於窗口的操作,需要指定2個參數:

  • window length - The duration of the window (3 in the figure)
  • slide interval - The interval at which the window-based operation is performed (2 in the figure). 
  1. 窗口大小,個人感覺是一段時間內數據的容器。

  2. 滑動間隔,就是我們可以理解的 cron 表達式吧。

窗口間隔一般大於(批處理間隔、滑動間隔)。這都是理解窗口操作的關鍵。

68、介紹一下Spark Streaming的foreachRDD(func)方法

將函數應用於 DStream 的 RDD 上,這個操作會輸出數據到外部系統,比如保存 RDD 到文件或者網絡數據庫等。需要注意的是 func 函數是運行該 Streaming 應用的 Driver 進程裏執行的。

69、簡單描述一下Spark Streaming的容錯原理

Spark Streaming 的一個特點就是高容錯。

首先 Spark RDD 就有容錯機制,每一個 RDD 都是不可變的分佈式可重算的數據集,其記錄這確定性的操作血統,所以只要輸入數據是可容錯的,那麼任意一個 RDD 的分區出錯或不可用,都是可以利用原始輸入數據通過轉換操作而重新計算出來的。

預寫日誌通常被用於數據庫和文件系統中,保證數據操作的持久性。預寫日誌通常是先將操作寫入到一個持久可靠的日誌文件中,然後纔對數據施加該操作,當加入施加操作中出現了異常,可以通過讀取日誌文件並重新施加該操作。

另外接收數據的正確性只在數據被預寫到日誌以後接收器纔會確認,已經緩存但還沒保存的數據可以在 Driver 重新啓動之後由數據源再發送一次,這兩個機制確保了零數據丟失,所有數據或者從日誌中恢復,或者由數據源重發。

70、DStream 有幾種轉換操作

Transform Operation、Window Operations、Join Operations

71、聊聊Spark Streaming的運行架構

https://mmbiz.qpic.cn/mmbiz_png/UdK9ByfMT2OSwS8tHQeMicc0egREicTZ5RxdvYTIReQqncsxUlDW5bMcW7hRKibAASOtAf7CzibG3kEplufYzTPdsQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1
72、說說DStreamGraph

Spark Streaming 中作業生成與 Spark 核心類似,對 DStream 進行的各種操作讓它們之間的操作會被記錄到名爲 DStream 使用輸出操作時,這些依賴關係以及它們之間的操作會被記錄到明偉 DStreamGraph 的對象中表示一個作業。這些作業註冊到 DStreamGraph 並不會立即運行,而是等到 Spark Streaming 啓動之後,達到批處理時間,才根據 DG 生成作業處理該批處理時間內接收的數據。

73、創建RDD的方式以及如何繼承創建RDD

Spark 可以從 Hadoop 支持的任何存儲源創建分佈式數據集,包括本地文件系統、HDFS、Cassandra、HBase、Amazon S3等。Spark 支持文本文件、SequenceFile 和任何其他 Hadoop InputFormat。可以使用SparkContext的textFile方法創建文本文件RDDs。

val distFile = sc.textFile("data.txt")

74、分析一下Spark Streaming的transform()和updateStateByKey()兩個操作

  • transform(func) 操作:允許 DStream 任意的 RDD-to-RDD 函數。

  • updateStateByKey 操作:可以保持任意狀態,同時進行信息更新,先定義狀態,後定義狀態更新函數。

75、說說Spark Streaming的輸出操作

print()、saveAsTextFiles(prefix, [suffix])、saveAsObjectFiles(prefix, [suffix])、saveAsHadoopFiles(prefix, [suffix])、foreachRDD(func)

76、談談Spark Streaming Driver端重啓會發生什麼

  1. 恢復計算:使用檢查點信息重啓 Driver 端,重構上下文並重啓接收器

  2. 恢復元數據塊:爲了保證能夠繼續下去所必備的全部元數據塊都被恢復

  3. 未完成作業的重新形成:由於失敗而沒有處理完成的批處理,將使用恢復的元數據再次產生 RDD 和對應的作業

  4. 讀取保存在日誌中的塊數據:在這些作業執行的時候,塊數據直接從預寫日誌中讀出,這將恢復在日誌中可靠地保存所有必要的數據

  5. 重發尚未確認的數據:失敗時沒有保存到日誌中的緩存數據將由數據源再次發送

77、再談Spark Streaming的容錯性

實時流處理系統需要長時間接收並處理數據,這個過程中出現異常是難以避免的,需要流程系統具備高容錯性。Spark Streaming 一開始就考慮了兩個方面。

  1. 利用 Spark 自身的容錯設計、存儲級別和 RDD 抽象設計能夠處理集羣中任何 Worker 節點的故障

  2. Spark 運行多種運行模式,其 Driver 端可能運行在 Master 節點或者集羣中的任意節點,這樣讓 Driver 端具備容錯能力是很大的挑戰,但是由於其接收的數據是按照批進行存儲和處理,這些批次數據的元數據可以通過執行檢查點的方式定期寫入到可靠的存儲中,在 Driver 端重新啓動中恢復這些狀態

當接收到的數據緩存在 Executor 內存中的丟失風險要怎麼處理呢?

如果是獨立運行模式/Yarn/Mesos 模式,當 Driver 端失敗的時候,該 Driver 端所管理的 Executor 以及內存中數據將終止,即時 Driver 端重新啓動這些緩存的數據也不能被恢復。爲了避免這種數據損失,就需要預寫日誌功能了。

當 Spark Streaming 應用開始的時候,也就是 Driver 開始的時候,接收器成爲長駐運行任務,這些接收器接收並保存流數據到 Spark 內存以供處理。

  1. 接收器將數據分成一系列小塊,存儲到 Executor 內存或磁盤中,如果啓動預寫日誌,數據同時還寫入到容錯文件系統的預寫日誌文件。

  2. 通知 StreamingContext,接收塊中的元數據被髮送到 Driver 的 StreamingContext,這個元數據包括兩種,一是定位其 Executor 內存或磁盤中數據位置的塊編號,二是塊數據在日誌中的偏移信息(如果啓用 WAL 的話)。

78、流數據如何存儲

作爲流數據接收器調用 Receiver.store 方式進行數據存儲,該方法有多個重載方法,如果數據量很小,則攢多條數據成數據塊再進行塊存儲,如果數據量大,則直接進行塊存儲。


79、StreamingContext啓動時序圖嗎

  1. 初始化 StreamingContext 中的 DStreamGraph 和 JobScheduler,進而啓動 JobScheduler 的 ReceiveTracker 和 JobGenerator。

  2. 初始化階段會進行成員變量的初始化,重要的包括 DStreamGraph(包含 DStream 之間相互依賴的有向無環圖),JobScheduler(定時查看 DStreamGraph,然後根據流入的數據生成運行作業),StreamingTab(在 Spark Streaming 運行的時候對流數據處理的監控)。

  3. 然後就是創建 InputDStream,接着就是對 InputDStream 進行 flatMap, map, reduceByKey, print 等操作,類似於 RDD 的轉換操作。

  4. 啓動 JobScheduler,實例化並啓動 ReceiveTracker 和 JobGenerator。

  5. 啓動 JobGenerator

  6. 啓動 ReceiverTracker

80、說說RDD和DataFrame和DataSet的關係

共性:

1、RDD、DataFrame、Dataset全都是spark平臺下的分佈式彈性數據集,爲處理超大型數據提供便利

2、三者都有惰性機制,在進行創建、轉換,如map方法時,不會立即執行,只有在遇到Action如foreach時,三者纔會開始遍歷運算,極端情況下,如果代碼裏面有創建、轉換,但是後面沒有在Action中使用對應的結果,在執行時會被直接跳過,如

1

2

3

4

5

6

7

8

val sparkconf = new SparkConf().setMaster("local").setAppName("test").set("spark.port.maxRetries","1000")

val spark = SparkSession.builder().config(sparkconf).getOrCreate()

val rdd=spark.sparkContext.parallelize(Seq(("a", 1), ("b", 1), ("a", 1)))

 

rdd.map{line=>

  println("運行")

  line._1

}

map中的println("運行")並不會運行

3、三者都會根據spark的內存情況自動緩存運算,這樣即使數據量很大,也不用擔心會內存溢出

4、三者都有partition的概念,如

1

2

3

4

5

6

7

8

var predata=data.repartition(24).mapPartitions{

      PartLine => {

        PartLine.map{

          line =>

             println(“轉換操作”)

                            }

                         }

這樣對每一個分區進行操作時,就跟在操作數組一樣,不但數據量比較小,而且可以方便的將map中的運算結果拿出來,如果直接用map,map中對外面的操作是無效的,如

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

val rdd=spark.sparkContext.parallelize(Seq(("a", 1), ("b", 1), ("a", 1)))

    var flag=0

    val test=rdd.map{line=>

      println("運行")

      flag+=1

      println(flag)

      line._1

    }

println(test.count)

println(flag)

    /**

    運行

    1

    運行

    2

    運行

    3

    3

    0

   * */

不使用partition時,對map之外的操作無法對map之外的變量造成影響

5、三者有許多共同的函數,如filter,sort等

6、在對DataFrame和Dataset進行操作許多操作都需要這個包進行支持

1

2

import spark.implicits._

//這裏的spark是SparkSession的變量名

7、DataFrame和Dataset均可使用模式匹配獲取各個字段的值和類型

DataFrame:

1

2

3

4

5

6

7

testDF.map{

      case Row(col1:String,col2:Int)=>

        println(col1);println(col2)

        col1

      case _=>

        ""

    }

爲了提高穩健性,最好後面有一個_通配操作,這裏提供了DataFrame一個解析字段的方法

Dataset:

1

2

3

4

5

6

7

8

case class Coltest(col1:String,col2:Int)extends Serializable //定義字段名和類型

    testDS.map{

      case Coltest(col1:String,col2:Int)=>

        println(col1);println(col2)

        col1

      case _=>

        ""

    }

  

區別:

RDD:

1、RDD一般和spark mlib同時使用

2、RDD不支持sparksql操作

DataFrame:

1、與RDD和Dataset不同,DataFrame每一行的類型固定爲Row,只有通過解析才能獲取各個字段的值,如

1

2

3

4

5

testDF.foreach{

  line =>

    val col1=line.getAs[String]("col1")

    val col2=line.getAs[String]("col2")

}

每一列的值沒法直接訪問

2、DataFrame與Dataset一般與spark ml同時使用

3、DataFrame與Dataset均支持sparksql的操作,比如select,groupby之類,還能註冊臨時表/視窗,進行sql語句操作,如

1

2

dataDF.createOrReplaceTempView("tmp")

spark.sql("select  ROW,DATE from tmp where DATE is not null order by DATE").show(100,false)

4、DataFrame與Dataset支持一些特別方便的保存方式,比如保存成csv,可以帶上表頭,這樣每一列的字段名一目瞭然

1

2

3

4

5

6

//保存

val saveoptions = Map("header" -> "true", "delimiter" -> "\t", "path" -> "hdfs://172.xx.xx.xx:9000/test")

datawDF.write.format("com.databricks.spark.csv").mode(SaveMode.Overwrite).options(saveoptions).save()

//讀取

val options = Map("header" -> "true", "delimiter" -> "\t", "path" -> "hdfs://172.xx.xx.xx:9000/test")

val datarDF= spark.read.options(options).format("com.databricks.spark.csv").load()

利用這樣的保存方式,可以方便的獲得字段名和列的對應,而且分隔符(delimiter)可以自由指定

Dataset:

這裏主要對比Dataset和DataFrame,因爲Dataset和DataFrame擁有完全相同的成員函數,區別只是每一行的數據類型不同

DataFrame也可以叫Dataset[Row],每一行的類型是Row,不解析,每一行究竟有哪些字段,各個字段又是什麼類型都無從得知,只能用上面提到的getAS方法或者共性中的第七條提到的模式匹配拿出特定字段

而Dataset中,每一行是什麼類型是不一定的,在自定義了case class之後可以很自由的獲得每一行的信息

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

case class Coltest(col1:String,col2:Int)extends Serializable //定義字段名和類型

/**

      rdd

      ("a", 1)

      ("b", 1)

      ("a", 1)

      * */

val test: Dataset[Coltest]=rdd.map{line=>

      Coltest(line._1,line._2)

    }.toDS

test.map{

      line=>

        println(line.col1)

        println(line.col2)

    }

可以看出,Dataset在需要訪問列中的某個字段時是非常方便的,然而,如果要寫一些適配性很強的函數時,如果使用Dataset,行的類型又不確定,可能是各種case class,無法實現適配,這時候用DataFrame即Dataset[Row]就能比較好的解決問題

轉化:

RDD、DataFrame、Dataset三者有許多共性,有各自適用的場景常常需要在三者之間轉換

DataFrame/Dataset轉RDD:

這個轉換很簡單

1

2

val rdd1=testDF.rdd

val rdd2=testDS.rdd

RDD轉DataFrame:

1

2

3

4

import spark.implicits._

val testDF = rdd.map {line=>

      (line._1,line._2)

    }.toDF("col1","col2")

一般用元組把一行的數據寫在一起,然後在toDF中指定字段名

RDD轉Dataset:

1

2

3

4

5

import spark.implicits._

case class Coltest(col1:String,col2:Int)extends Serializable //定義字段名和類型

val testDS = rdd.map {line=>

      Coltest(line._1,line._2)

    }.toDS

可以注意到,定義每一行的類型(case class)時,已經給出了字段名和類型,後面只要往case class裏面添加值即可

Dataset轉DataFrame:

這個也很簡單,因爲只是把case class封裝成Row

1

2

import spark.implicits._

val testDF = testDS.toDF

DataFrame轉Dataset:

1

2

3

import spark.implicits._

case class Coltest(col1:String,col2:Int)extends Serializable //定義字段名和類型

val testDS = testDF.as[Coltest]

這種方法就是在給出每一列的類型後,使用as方法,轉成Dataset,這在數據類型是DataFrame又需要針對各個字段處理時極爲方便

特別注意:

在使用一些特殊的操作時,一定要加上 import spark.implicits._ 不然toDF、toDS無法使用

 

 

 

 

 

 

 

 

 

 

附錄:

獨孤九劍-Spark面試80連擊(上)

獨孤九劍-Spark面試80連擊(下)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章