大數(shù)據(jù)開發(fā)面試之26個Spark高頻考點

前言
        今天為大家?guī)泶髷?shù)據(jù)開發(fā)面試中,關于 Spark 的 28 個高頻考點 。另外文末為大家準備了福利,不要錯過 !

1、Spark 如何保證宕機迅速恢復?
        適當增加 spark standby master

        編寫 shell 腳本,定期檢測 master 狀態(tài),出現(xiàn)宕機后對 master 進行重啟操作

2. Spark streaming 以及基本工作原理?
        Spark streaming 是 spark core API 的一種擴展,可以用于進行大規(guī)模、高吞吐量、容錯的實時數(shù)據(jù)流的處理。

        它支持從多種數(shù)據(jù)源讀取數(shù)據(jù),比如 Kafka、Flume、Twitter 和 TCP Socket,并且能夠使用算子比如 map、reduce、join 和 window 等來處理數(shù)據(jù),處理后的數(shù)據(jù)可以保存到文件系統(tǒng)、數(shù)據(jù)庫等存儲中。

        Spark streaming 內(nèi)部的基本工作原理是:接受實時輸入數(shù)據(jù)流,然后將數(shù)據(jù)拆分成 batch ,比如每收集一秒的數(shù)據(jù)封裝成一個 batch,然后將每個 batch 交給 spark 的計算引擎進行處理,最后會生產(chǎn)出一個結果數(shù)據(jù)流,其中的數(shù)據(jù)也是一個一個的 batch 組成的 。

3. Spark 有哪些組件?
        master:管理集群和節(jié)點,不參與計算。

       worker:計算節(jié)點,進程本身不參與計算,和 master 匯報。Driver:運行程序的 main 方法,創(chuàng)建 spark context 對象。

        spark context:控制整個 application 的生命周期,包括 dagsheduler 和 task scheduler 等組件。

        client:用戶提交程序的入口。

4、Spark 工作機制
        用戶在 client 端提交作業(yè)后,會由 Driver 運行 main 方法并創(chuàng)建 spark context 上下文。執(zhí)行 add 算子,形成 dag 圖輸入 dagscheduler,按照 add 之間的依賴關系劃分 stage 輸入 task scheduler。task scheduler 會將 stage 劃分為 task set 分發(fā)到各個節(jié)點的 executor 中執(zhí)行。

5、Spark 主備切換機制原理知道嗎?
        Master 實際上可以配置兩個,Spark 原生的 standalone 模式是支持 Master 主備切換的。當 Active Master 節(jié)點掛掉以后,可以將 Standby Master 切換為 Active Master。

        Spark Master 主備切換可以基于兩種機制,一種是基于文件系統(tǒng)的,一種是基于 ZooKeeper 的。

        基于文件系統(tǒng)的主備切換機制,需要在 Active Master 掛掉之后手動切換到 Standby Master 上;而基于 Zookeeper 的主備切換機制,可以實現(xiàn)自動切換 Master 。

6. Spark 的有幾種部署模式,每種模式特點?
1)本地模式       

        Spark 不一定非要跑在 hadoop 集群,可以在本地,起多個線程的方式來指定。將 Spark 應用以多線程的方式直接運行在本地,一般都是為了方便調(diào)試 。

        本地模式分三類

local     :只啟動一個 executor
local[k] :啟動 k 個 executor
local[*] :啟動跟 cpu 數(shù)目相同的 executor
2)standalone 模式

        分布式部署集群,自帶完整的服務,資源管理和任務監(jiān)控是 Spark 自己監(jiān)控,這個模式也是其他模式的基礎 。

3)Spark on yarn 模式

        分布式部署集群,資源和任務監(jiān)控交給 yarn 管理,但是目前僅支持粗粒度資源分配方式,包含 cluster 和 client 運行模式,cluster 適合生產(chǎn),driver 運行在集群子節(jié)點,具有容錯功能,client 適合調(diào)試,dirver 運行在客戶端。

4)Spark On Mesos 模式

        官方推薦這種模式(當然,原因之一是血緣關系)。正是由于 Spark 開發(fā)之初就考慮到支持 Mesos,因此,目前而言,Spark 運行在 Mesos 上會比運行在 YARN 上更加靈活,更加自然。用戶可選擇兩種調(diào)度模式之一運行自己的應用程序:

粗粒度模式(Coarse-grained Mode):每個應用程序的運行環(huán)境由一個 Dirver 和若干個 Executor 組成,其中,每個 Executor 占用若干資源,內(nèi)部可運行多個 Task(對應多少個“slot”)。應用程序的各個任務正式運行之前,需要將運行環(huán)境中的資源全部申請好,且運行過程中要一直占用這些資源,即使不用,最后程序運行結束后,回收這些資源。

細粒度模式(Fine-grained Mode):鑒于粗粒度模式會造成大量資源浪費,Spark On Mesos 還提供了另外一種調(diào)度模式:細粒度模式,這種模式類似于現(xiàn)在的云計算,思想是按需分配 。

7、Spark 為什么比 mapreduce 快?
        1)基于內(nèi)存計算,減少低效的磁盤交互;

        2)高效的調(diào)度算法,基于 DAG;

        3)容錯機制 Linage,精華部分就是 DAG 和 Lingae

8、簡單說一下 hadoop 和 spark 的 shuffle 相同和差異?
        1)從 high-level 的角度來看,兩者并沒有大的差別。都是將 mapper (Spark 里是 ShuffleMapTask)的輸出進行 partition,不同的 partition 送到不同的 reducer(Spark 里 reducer 可能是下一個 stage 里的 ShuffleMapTask,也可能是 ResultTask)。Reducer 以內(nèi)存作緩沖區(qū),邊 shuffle 邊 aggregate 數(shù)據(jù),等到數(shù)據(jù) aggregate 好以后進行 reduce() (Spark 里可能是后續(xù)的一系列操作)。

        2)從 low-level 的角度來看,兩者差別不小。Hadoop MapReduce 是 sort-based,進入 combine() 和 reduce() 的 records 必須先 sort。這樣的好處在于 combine/reduce() 可以處理大規(guī)模的數(shù)據(jù),因為其輸入數(shù)據(jù)可以通過外排得到(mapper 對每段數(shù)據(jù)先做排序,reducer 的 shuffle 對排好序的每段數(shù)據(jù)做歸并)。目前的 Spark 默認選擇的是 hash-based,通常使用HashMap 來對 shuffle 來的數(shù)據(jù)進行 aggregate,不會對數(shù)據(jù)進行提前排序。如果用戶需要經(jīng)過排序的數(shù)據(jù),那么需要自己調(diào)用類似 sortByKey() 的操作;如果你是 Spark 1.1 的用戶,可以將 spark.shuffle.manager 設置為 sort,則會對數(shù)據(jù)進行排序。在 Spark 1.2 中,sort 將作為默認的 Shuffle 實現(xiàn)。

        3)從實現(xiàn)角度來看,兩者也有不少差別。Hadoop MapReduce 將處理流程劃分出明顯的幾個階段:map(), spill, merge, shuffle, sort, reduce() 等。每個階段各司其職,可以按照過程式的編程思想來逐一實現(xiàn)每個階段的功能。在 Spark 中,沒有這樣功能明確的階段,只有不同的 stage 和一系列的 transformation(),所以 spill, merge, aggregate 等操作需要蘊含在 transformation() 中。如果我們將 map 端劃分數(shù)據(jù)、持久化數(shù)據(jù)的過程稱為 shuffle write,而將reducer 讀入數(shù)據(jù)、aggregate 數(shù)據(jù)的過程稱為 shuffle read。那么在 Spark 中,問題就變?yōu)樵趺丛?job 的邏輯或者物理執(zhí)行圖中加入 shuffle write 和 shuffle read 的處理邏輯?以及兩個處理邏輯應該怎么高效實現(xiàn)?Shuffle write 由于不要求數(shù)據(jù)有序,shuffle write 的任務很簡單:將數(shù)據(jù) partition 好,并持久化。之所以要持久化,一方面是要減少內(nèi)存存儲空間壓力,另一方面也是為了 fault-tolerance。

9. spark 工作機制
        ① 構建 Application 的運行環(huán)境,Driver 創(chuàng)建一個 SparkContext

        ② SparkContext 向資源管理器(Standalone、Mesos、Yarn)申請Executor 資源,資源管理器啟動 StandaloneExecutorbackend(Executor)

        ③ Executor 向 SparkContext 申請 Task

        ④ SparkContext 將應用程序分發(fā)給 Executor

        ⑤ SparkContext 就建成 DAG 圖,DAGScheduler 將 DAG 圖解析成 Stage,每個 Stage 有多個 task,形成 taskset 發(fā)送給 task Scheduler,由task Scheduler 將 Task 發(fā)送給 Executor 運行

        ⑥ Task 在 Executor 上運行,運行完釋放所有資源

10、Spark 的優(yōu)化怎么做?
        spark 調(diào)優(yōu)比較復雜,但是大體可以分為三個方面來進行:

        1)平臺層面的調(diào)優(yōu):防止不必要的 jar 包分發(fā),提高數(shù)據(jù)的本地性,選擇高效的存儲格式如 parquet

        2)應用程序層面的調(diào)優(yōu):過濾操作符的優(yōu)化降低過多小任務,降低單條記錄的資源開銷,處理數(shù)據(jù)傾斜,復用 RDD 進行緩存,作業(yè)并行化執(zhí)行等等

        3)JVM 層面的調(diào)優(yōu):設置合適的資源量,設置合理的 JVM,啟用高效的序列化方法如 kyro,增大 off head 內(nèi)存等等

11、數(shù)據(jù)本地性是在哪個環(huán)節(jié)確定的?
        具體的 task 運行在哪臺機器上,是由 dag 劃分 stage 的時候確定的

12、RDD 的彈性表現(xiàn)在哪幾點?
      1)自動的進行內(nèi)存和磁盤的存儲切換;               

        2)基于 Lineage 的高效容錯;

        3)task 如果失敗會自動進行特定次數(shù)的重試;

        4)stage 如果失敗會自動進行特定次數(shù)的重試,而且只會計算失敗的分片;

        5)checkpoint 和 persist,數(shù)據(jù)計算之后持久化緩存;

        6)數(shù)據(jù)調(diào)度彈性,DAG TASK 調(diào)度和資源無關;

        7)數(shù)據(jù)分片的高度彈性。

13、RDD 有哪些缺陷?
        1)不支持細粒度的寫和更新操作(如網(wǎng)絡爬蟲),spark 寫數(shù)據(jù)是粗粒度的。所謂粗粒度,就是批量寫入數(shù)據(jù),為了提高效率。但是讀數(shù)據(jù)是細粒度的也就是說可以一條條的讀 。

        2)不支持增量迭代計算,但是 Flink 支持

14、Spark 的 shuffle 過程?
        可以從下面三點去展開:

shuffle 過程的劃分
shuffle 的中間結果如何存儲
shuffle 的數(shù)據(jù)如何拉取過來
15、Spark 的數(shù)據(jù)本地性有哪幾種?
        Spark 中的數(shù)據(jù)本地性有三種:

PROCESS_LOCAL 是指讀取緩存在本地節(jié)點的數(shù)據(jù)
NODE_LOCAL 是指讀取本地節(jié)點硬盤數(shù)據(jù)
ANY 是指讀取非本地節(jié)點數(shù)據(jù)
        通常讀取數(shù)據(jù) PROCESS_LOCAL>NODE_LOCAL>ANY,盡量使數(shù)據(jù)以 PROCESS_LOCAL 或 NODE_LOCAL 方式讀取。其中 PROCESS_LOCAL 還和 cache 有關,如果 RDD 經(jīng)常用的話將該 RDD cache 到內(nèi)存中,注意,由于cache 是 lazy 的,所以必須通過一個 action 的觸發(fā),才能真正的將該 RDD cache 到內(nèi)存中。

16、Spark 為什么要持久化,一般什么場景下要進行 persist 操作?
         為什么要進行持久化?

         spark 所有復雜一點的算法都會有 persist 身影,spark 默認數(shù)據(jù)放在內(nèi)存,spark 很多內(nèi)容都是放在內(nèi)存的,非常適合高速迭代,1000 個步驟只有第一個輸入數(shù)據(jù),中間不產(chǎn)生臨時數(shù)據(jù),但分布式系統(tǒng)風險很高,所以容易出錯,就要容錯,rdd 出錯或者分片可以根據(jù)血統(tǒng)算出來,如果沒有對父 rdd 進行 persist 或者 cache 的話,就需要重頭做。正常情況下,以下場景會使用 persist:

某個步驟計算非常耗時,需要進行 persist 持久化
計算鏈條非常長,重新恢復要算很多步驟,很好使,persist
checkpoint 所在的 rdd 要持久化 persist。checkpoint 前,要持久化,寫個 rdd.cache 或者 rdd.persist,將結果保存起來,再寫 checkpoint 操作,這樣執(zhí)行起來會非???,不需要重新計算 rdd 鏈條了。checkpoint 之前一定會進行 persist 。
shuffle 之后要 persist,shuffle 要進行網(wǎng)絡傳輸,風險很大,數(shù)據(jù)丟失重來,恢復代價很大
shuffle 之前進行 persist,框架默認將數(shù)據(jù)持久化到磁盤,這個是框架自動做的
17.介紹一下 join 操作優(yōu)化經(jīng)驗?
         join 其實常見的就分為兩類:map-side join 和 reduce-side join。當大表和小表 join 時,用 map-side join 能顯著提高效率。將多份數(shù)據(jù)進行關聯(lián)是數(shù)據(jù)處理過程中非常普遍的用法,不過在分布式計算系統(tǒng)中,這個問題往往會變得非常麻煩,因為框架提供的 join 操作一般會將所有數(shù)據(jù)根據(jù) key 發(fā)送到所有的 reduce 分區(qū)中去,也就是 shuffle 的過程。造成大量的網(wǎng)絡以及磁盤 IO 消耗,運行效率極其低下,這個過程一般被稱為 reduce-side-join。如果其中有張表較小的話,我們則可以自己實現(xiàn)在 map 端實現(xiàn)數(shù)據(jù)關聯(lián),跳過大量數(shù)據(jù)進行 shuffle 的過程,運行時間得到大量縮短,根據(jù)不同數(shù)據(jù)可能會有幾倍到數(shù)十倍的性能提升 。

18、描述 Yarn 執(zhí)行一個任務的過程?
客戶端 client 向 ResouceManager 提交 Application,ResouceManager 接受 Application 并根據(jù)集群資源狀況選取一個 node 來啟動 Application 的任務調(diào)度器 driver(ApplicationMaster)
ResouceManager 找到那個 node,命令其該 node 上的 nodeManager來啟動一個新的 JVM 進程運行程序的 driver(ApplicationMaster)部分,driver(ApplicationMaster)啟動時會首先向 ResourceManager 注冊,說明由自己來負責當前程序的運行
driver(ApplicationMaster)開始下載相關 jar 包等各種資源,基于下載的 jar 等信息決定ResourceManager 申請具體的資源內(nèi)容。
ResouceManager 接受到 driver(ApplicationMaster)提出的申請后,會最大化地滿足 資源分配請求,并發(fā)送資源的元數(shù)據(jù)信息給 driver(ApplicationMaster)
driver(ApplicationMaster)收到發(fā)過來的資源元數(shù)據(jù)信息后會根據(jù)元數(shù)據(jù)信息發(fā)指令給具體機器上的NodeManager,讓其啟動具體的 container。
NodeManager 收到 driver 發(fā)來的指令,啟動 container,container 啟動后必須向 driver(ApplicationMaster)注冊。
driver(ApplicationMaster)收到 container 的注冊,開始進行任務的調(diào)度和計算,直到 任務完成。
        需要注意一個細節(jié):注意:如果 ResourceManager 第一次沒有能夠滿足 driver(ApplicationMaster)的資源請求 ,后續(xù)發(fā)現(xiàn)有空閑的資源,會主動向 driver(ApplicationMaster)發(fā)送可用資源的元數(shù)據(jù)信息以提供更多的資源用于當前程序的運行。

19、Spark on Yarn 模式有哪些優(yōu)點?
與其他計算框架共享集群資源(Spark 框架與 MapReduce 框架同時運行,如果不用 Yarn 進行資源分配,MapReduce 分到的內(nèi)存資源會很少,效率低下);資源按需分配,進而提高集群資源利用等 。
相較于 Spark 自帶的 Standalone 模式,Yarn 的資源分配更加細致 。
Application 部署簡化,例如 Spark,Storm 等多種框架的應用由客戶端提交后,由 Yarn 負責資源的管理和調(diào)度,利用 Container 作為資源隔離的單位,以它為單位去使用內(nèi)存,cpu 等 。
Yarn 通過隊列的方式,管理同時運行在 Yarn 集群中的多個服務,可根據(jù)不同類型的應用程序負載情況,調(diào)整對應的資源使用量,實現(xiàn)資源彈性管理 。
20、談談你對 container 的理解?
Container 作為資源分配和調(diào)度的基本單位,其中封裝了的資源如內(nèi)存,CPU,磁盤,網(wǎng)絡帶寬等。目前 yarn 僅僅封裝內(nèi)存和 CPU 。
Container 由 ApplicationMaster 向 ResourceManager 申請的,由 ResouceManager 中的資源調(diào)度器異步分配給 ApplicationMaster
Container 的運行是由 ApplicationMaster 向資源所在的 NodeManager 發(fā)起的,Container 運行時需提供內(nèi)部執(zhí)行的任務命令
21、Spark 使用 parquet 文件存儲格式能帶來哪些好處?
如果說 HDFS 是大數(shù)據(jù)時代分布式文件系統(tǒng)首選標準,那么 parquet 則是整個大數(shù)據(jù)時代文件存儲格式實時首選標準 。
速度更快:從使用 spark sql 操作普通文件 CSV 和 parquet 文件速度對比上看,絕大多數(shù)情況會比使用 csv 等普通文件速度提升 10 倍左右,在一些普通文件系統(tǒng)無法在 spark 上成功運行的情況下,使用 parquet 很多時候可以成功運行。
parquet 的壓縮技術非常穩(wěn)定出色,在 spark sql 中對壓縮技術的處理可能無法正常的完成工作(例如會導致 lost task,lost executor)但是此時如果使用 parquet 就可以正常的完成
極大的減少磁盤 I/o,通常情況下能夠減少 75%的存儲空間,由此可以極大的減少 spark sql 處理數(shù)據(jù)的時候的數(shù)據(jù)輸入內(nèi)容,尤其是在 spark1.6x 中有個下推過濾器在一些情況下可以極大的減少磁盤的 IO 和內(nèi)存的占用。
spark 1.6x 中 parquet 方式極大的提升了掃描的吞吐量,極大提高了數(shù)據(jù)的查找速度 spark1.6 和 spark1.5x 相比而言,提升了大約 1 倍的速度,在spark1.6X 中,操作 parquet 時候 cpu 也進行了極大的優(yōu)化,有效的降低了cpu 消耗。
采用 parquet 可以極大地優(yōu)化 spark 的調(diào)度和執(zhí)行。經(jīng)過測試 spark 如果用 parquet 可以有效的減少 stage 的執(zhí)行消耗,同時可以優(yōu)化執(zhí)行路徑
22、介紹 parition 和 block 有什么關聯(lián)關系?
hdfs 中的 block 是分布式存儲的最小單元,等分,可設置冗余,這樣設計有一部分磁盤空間的浪費,但是整齊的 block 大小,便于快速找到、讀取對應的內(nèi)容;
Spark 中的 partion 是彈性分布式數(shù)據(jù)集 RDD 的最小單元,RDD 是由分布在各個節(jié)點上的 partion 組成的。partion 是指的 spark 在計算過程中,生成的數(shù)據(jù)在計算空間內(nèi)最小單元,同一份數(shù)據(jù)(RDD)的 partion 大小不一,數(shù)量不定,是根據(jù) application 里的算子和最初讀入的數(shù)據(jù)分塊數(shù)量決定;
block 位于存儲空間、partion 位于計算空間,block 的大小是固定的、partion 大小是不固定的,是從 2 個不同的角度去看數(shù)據(jù)
23、Spark 應用程序的執(zhí)行過程是什么?
構建 Spark Application 的運行環(huán)境(啟動 SparkContext),SparkContext 向資源管理器(可以是 Standalone、Mesos 或 YARN)注冊并申請運行 Executor 資源;
資源管理器分配 Executor 資源并啟動 StandaloneExecutorBackend,Executor 運行情況將隨著心跳發(fā)送到資源管理器上;
SparkContext 構建成 DAG 圖,將 DAG 圖分解成 Stage,并把 Taskset發(fā)送給 Task Scheduler。Executor 向 SparkContext 申請 Task,Task Scheduler 將 Task 發(fā)放給 Executor 運行同時 SparkContext 將應用程序代碼發(fā)放給 Executor;
Task 在 Executor 上運行,運行完畢釋放所有資源 。
24、不需要排序的 hash shuffle 是否一定比需要排序的 sort shuffle 速度快?
        不一定,當數(shù)據(jù)規(guī)模小,Hash shuffle 快于 Sorted Shuffle ;數(shù)據(jù)規(guī)模大的時候,sorted Shuffle 會比 Hash shuffle 快很多 。因為數(shù)量大的有很多小文件,不均勻,甚至出現(xiàn)數(shù)據(jù)傾斜,消耗內(nèi)存大,1.x 之前 spark 使用 hash,適合處理中小規(guī)模,1.x 之后,增加了 Sorted shuffle,Spark 更能勝任大規(guī)模處理了 。

25、Sort-based shuffle 的缺陷?
如果 mapper 中 task 的數(shù)量過大,依舊會產(chǎn)生很多小文件,此時在 shuffle 傳遞數(shù)據(jù)的過程中 reducer 段,reduce 會需要同時大量的記錄進行反序列化,導致大量的內(nèi)存消耗和 GC 的巨大負擔,造成系統(tǒng)緩慢甚至崩潰。
如果需要在分片內(nèi)也進行排序,此時需要進行 mapper 段和 reducer 段的兩次排序。
26、spark.storage.memoryFraction 參數(shù)的含義,實際生產(chǎn)中如何調(diào)優(yōu)?
用于設置 RDD 持久化數(shù)據(jù)在 Executor 內(nèi)存中能占的比例,默認是 0.6,,默認 Executor 60%的內(nèi)存,可以用來保存持久化的 RDD 數(shù)據(jù)。根據(jù)你選擇的不同的持久化策略,如果內(nèi)存不夠時,可能數(shù)據(jù)就不會持久化,或者數(shù)據(jù)會寫入磁盤;
如果持久化操作比較多,可以提高 spark.storage.memoryFraction 參數(shù),使得更多的持久化數(shù)據(jù)保存在內(nèi)存中,提高數(shù)據(jù)的讀取性能,如果 shuffle 的操作比較多,有很多的數(shù)據(jù)讀寫操作到 JVM 中,那么應該調(diào)小一點,節(jié)約出更多的內(nèi)存給 JVM,避免過多的 JVM gc 發(fā)生。在 WEB UI 中觀察如果發(fā)現(xiàn) gc 時間很長,可以設置 spark.storage.memoryFraction 更小一點。
小結
        本篇文章總結了在大數(shù)據(jù)開發(fā)面試過程中關于 Spark 的核心知識點 。大家不用去硬記,但至少被問到了需要有印象且能按照正確的思路去回答 。下周我將為大家?guī)砀佑埠说母韶?,歡迎持續(xù)關注,我們下期不見不散!

作者:夢想家 Alex


歡迎關注:大數(shù)據(jù)夢想家