Spark 經(jīng)典面試題匯總《一》
1你是怎么理解Spark,它的特點(diǎn)是什么?
Spark是一個(gè)基于內(nèi)存的,用于大規(guī)模數(shù)據(jù)處理(離線計(jì)算、實(shí)時(shí)計(jì)算、快速查詢(交互式查詢))的統(tǒng)一分析引擎。
它內(nèi)部的組成模塊,包含SparkCore,SparkSQL,Spark Streaming,SparkMLlib,SparkGraghx等。
Spark的主要特點(diǎn)包括:
快:Spark計(jì)算速度是MapReduce計(jì)算速度的10-100倍,Spark使用內(nèi)存計(jì)算技術(shù),以及基于彈性分布式數(shù)據(jù)集(RDD)的計(jì)算模型,可以在內(nèi)存中對數(shù)據(jù)進(jìn)行高效處理,從而比傳統(tǒng)的基于磁盤的計(jì)算系統(tǒng)更快速。
容錯(cuò)性:Spark可以在節(jié)點(diǎn)故障時(shí)重新計(jì)算丟失的數(shù)據(jù),從而避免了數(shù)據(jù)丟失的問題,保證了任務(wù)的可靠性。
多語言支持:Spark提供了多種編程語言API,包括Java、Scala、Python和R等,使得開發(fā)者可以使用自己熟悉的語言進(jìn)行數(shù)據(jù)處理任務(wù)。
數(shù)據(jù)處理能力:Spark可以處理各種類型的數(shù)據(jù),包括結(jié)構(gòu)化數(shù)據(jù)、半結(jié)構(gòu)化數(shù)據(jù)和非結(jié)構(gòu)化數(shù)據(jù)等,并且支持各種數(shù)據(jù)源的讀寫操作,如HDFS、Hive、MySQL等。
可擴(kuò)展性:Spark可以在大規(guī)模集群上運(yùn)行,支持自動(dòng)分區(qū)和并行化處理,從而可以處理PB級別的數(shù)據(jù)。
總的來說,Spark具有高效的性能、容錯(cuò)性、多語言支持、強(qiáng)大的數(shù)據(jù)處理能力和良好的可擴(kuò)展性,適用于各種大規(guī)模數(shù)據(jù)處理任務(wù),如機(jī)器學(xué)習(xí)、圖像處理、數(shù)據(jù)挖掘、日志分析等。
2Spark有幾種部署方式,請分別簡要論述?
Spark有三種常見的部署方式,分別是本地模式、單例模式和Yarn模式。
1) Local:運(yùn)行在一臺機(jī)器上,通常是練手或者測試環(huán)境。
2)Standalone:構(gòu)建一個(gè)基于Mster+Slaves的資源調(diào)度集群,Spark任務(wù)提交給Master運(yùn)行。是Spark自身的一個(gè)調(diào)度系統(tǒng)。
3)Yarn: Spark客戶端直接連接Yarn,不需要額外構(gòu)建Spark集群。有yarn-client和yarn-cluster兩種模式,主要區(qū)別在于:Driver程序的運(yùn)行節(jié)點(diǎn)。
3Spark提交作業(yè)的參數(shù)
因?yàn)槲覀僑park任務(wù)是采用的Shell腳本進(jìn)行提交,所以一定會涉及到幾個(gè)重要的參數(shù),而這個(gè)也是在面試的時(shí)候容易被考察到的“細(xì)節(jié)”。
executor-cores —— 每個(gè)executor使用的內(nèi)核數(shù),默認(rèn)為1,官方建議2-5個(gè),我們企業(yè)是4個(gè)
num-executors —— 啟動(dòng)executors的數(shù)量,默認(rèn)為2
executor-memory —— executor內(nèi)存大小,默認(rèn)1G
driver-cores —— driver使用內(nèi)核數(shù),默認(rèn)為1
driver-memory —— driver內(nèi)存大小,默認(rèn)512M
在使用Spark提交作業(yè)時(shí),可以使用以下參數(shù):
--class:指定要運(yùn)行的主類名。
--master:指定要連接的Spark集群的URL。例如,--master yarn將連接到Y(jié)ARN集群。
--deploy-mode:指定作業(yè)的部署模式,可選值為client或cluster。client模式是在客戶端啟動(dòng)Driver程序,cluster模式則是在集群中啟動(dòng)Driver程序。
--executor-memory:指定每個(gè)Executor可用的內(nèi)存量。例如,--executor-memory 4g將為每個(gè)Executor分配4GB內(nèi)存。
--num-executors:指定要啟動(dòng)的Executor數(shù)量。例如,--num-executors 10將啟動(dòng)10個(gè)Executor。
--executor-cores:指定每個(gè)Executor可用的CPU核心數(shù)量。例如,--executor-cores 2將為每個(gè)Executor分配2個(gè)CPU核心。
--conf:用于設(shè)置Spark配置屬性。例如,--conf spark.shuffle.compress=true將啟用Shuffle壓縮。
--jars:用于指定需要在作業(yè)中使用的JAR文件。例如,--jars /path/to/jar1,/path/to/jar2將加載jar1和jar2。
--files:用于指定需要在作業(yè)中使用的文件。例如,--files /path/to/file1,/path/to/file2將加載file1和file2。
更多參數(shù)和說明可以通過運(yùn)行spark-submit --help來查看。
4簡述Spark的作業(yè)提交流程
Spark的任務(wù)提交方式實(shí)際上有兩種,分別是YarnClient模式和YarnCluster模式。大家在回答這個(gè)問題的時(shí)候,也需要分類去介紹。千萬不要被冗長的步驟嚇到,一定要學(xué)會總結(jié)差異,發(fā)現(xiàn)規(guī)律,通過圖形去增強(qiáng)記憶。
YarnClient 運(yùn)行模式介紹
YarnCluster 模式介紹
Spark作業(yè)的提交流程如下:
編寫應(yīng)用程序:首先需要編寫Spark應(yīng)用程序。這通常是一個(gè)基于Spark API的Scala、Java或Python腳本,它定義了數(shù)據(jù)處理的流程和邏輯。
打包應(yīng)用程序:使用構(gòu)建工具(如sbt、Maven或Gradle)將應(yīng)用程序打包成JAR文件。
準(zhǔn)備環(huán)境:確保集群中的所有節(jié)點(diǎn)都安裝了相同版本的Spark,并且應(yīng)用程序所需的所有依賴項(xiàng)都已安裝。
啟動(dòng)Spark集群:在集群的一個(gè)節(jié)點(diǎn)上啟動(dòng)Spark Master,然后啟動(dòng)一些Spark Worker進(jìn)程。
提交作業(yè):使用spark-submit命令提交應(yīng)用程序,命令包括應(yīng)用程序的JAR文件和一些參數(shù),例如作業(yè)名稱、Master節(jié)點(diǎn)URL、作業(yè)配置等。
分配資源:Spark提交器將根據(jù)作業(yè)配置、集群資源可用性和其他因素,將作業(yè)分配給可用的Worker節(jié)點(diǎn),并為每個(gè)Executor分配資源。
運(yùn)行任務(wù):一旦資源分配完成,Spark將啟動(dòng)Driver程序,并將作業(yè)的任務(wù)發(fā)送給Worker節(jié)點(diǎn)上的Executor進(jìn)行執(zhí)行。
監(jiān)控進(jìn)度:Spark Web UI提供了有關(guān)作業(yè)進(jìn)度和性能的實(shí)時(shí)監(jiān)控信息,可以用于診斷問題或調(diào)整配置。
收集結(jié)果:一旦作業(yè)完成,Spark會將結(jié)果收集并返回給Driver程序。在驅(qū)動(dòng)程序中,可以將結(jié)果保存到外部存儲系統(tǒng)或進(jìn)行進(jìn)一步處理。
總之,Spark作業(yè)提交流程是一個(gè)復(fù)雜的過程,需要綜合考慮應(yīng)用程序、集群配置和資源分配等多個(gè)因素。熟練掌握Spark作業(yè)提交流程是成為一名優(yōu)秀Spark開發(fā)人員的關(guān)鍵。
5你是如何理解Spark中血統(tǒng)(RDD)的概念?它的作用是什么?
概念
RDD是彈性分布式數(shù)據(jù)集,是Spark中最基本的數(shù)據(jù)抽象,代表一個(gè)不可變、可分區(qū)、里面的元素可并行計(jì)算 的集合。
作用
提供了一個(gè)抽象的數(shù)據(jù)模型,將具體的應(yīng)用邏輯表達(dá)為一系列轉(zhuǎn)換操作(函數(shù))。另外不同RDD之間的轉(zhuǎn)換操作之間還可以形成依賴關(guān)系,進(jìn)而實(shí)現(xiàn)管道化,從而避免了中間結(jié)果的存儲,大大降低了數(shù)據(jù)復(fù)制、磁盤IO和序列化開銷,并且還提供了更多的API(map/reduec/filter/groupBy...)
特點(diǎn)
容錯(cuò)性:RDD具有容錯(cuò)性,因?yàn)樗鼤詣?dòng)將數(shù)據(jù)劃分成多個(gè)分區(qū),并在集群中的多個(gè)節(jié)點(diǎn)上進(jìn)行復(fù)制,從而實(shí)現(xiàn)數(shù)據(jù)的高可靠性和容錯(cuò)性。
數(shù)據(jù)共享:RDD允許多個(gè)并行操作共享相同的數(shù)據(jù)集合,以便在不同的計(jì)算步驟中復(fù)用數(shù)據(jù),從而避免了重復(fù)的IO操作,提高了計(jì)算效率。
優(yōu)化計(jì)算:RDD通過支持多個(gè)轉(zhuǎn)換操作和行動(dòng)操作,允許進(jìn)行復(fù)雜的計(jì)算和數(shù)據(jù)分析,同時(shí)也支持對計(jì)算過程進(jìn)行優(yōu)化,以便最大限度地減少計(jì)算成本。
血統(tǒng)跟蹤:RDD通過記錄其前一個(gè)RDD的依賴關(guān)系,構(gòu)建了一個(gè)有向無環(huán)圖(DAG)來跟蹤其數(shù)據(jù)處理流程,從而允許Spark在節(jié)點(diǎn)故障時(shí)重新計(jì)算丟失的分區(qū),實(shí)現(xiàn)了彈性計(jì)算。
血統(tǒng)是指RDD之間的依賴關(guān)系,這種依賴關(guān)系可以通過DAG(有向無環(huán)圖)來表示。每個(gè)RDD都會記錄其父RDD的引用和產(chǎn)生該RDD的轉(zhuǎn)換操作,這樣,如果某個(gè)RDD的分區(qū)丟失或出現(xiàn)故障,Spark可以根據(jù)血統(tǒng)信息重新計(jì)算該RDD的丟失分區(qū),實(shí)現(xiàn)了彈性計(jì)算。因此,RDD的血統(tǒng)跟蹤是Spark實(shí)現(xiàn)容錯(cuò)性的重要機(jī)制。
6簡述Spark的寬窄依賴,以及Spark如何劃分stage,每個(gè)stage又根據(jù)什么決定task個(gè)數(shù)?
spark的寬窄依賴問題是SparkCore部分的重點(diǎn)考察內(nèi)容,多數(shù)出現(xiàn)在筆試中,大家需要注意。
窄依賴:父RDD的一個(gè)分區(qū)只會被子RDD的一個(gè)分區(qū)依賴
寬依賴:父RDD的一個(gè)分區(qū)會被子RDD的多個(gè)分區(qū)依賴(涉及到shuffle)
那Stage是如何劃分的呢?
根據(jù)RDD之間的依賴關(guān)系的不同將Job劃分成不同的Stage,遇到一個(gè)寬依賴則劃分一個(gè)Stage。
每個(gè)stage又根據(jù)什么決定task個(gè)數(shù)?
Stage是一個(gè)TaskSet,將Stage根據(jù)分區(qū)數(shù)劃分成一個(gè)個(gè)的Task。
這里為了方便大家理解,貼上一張過程圖
Spark中的寬依賴和窄依賴是指RDD之間的依賴關(guān)系類型。在Spark中,每個(gè)RDD都有一個(gè)或多個(gè)父RDD和一個(gè)或多個(gè)子RDD,RDD之間的依賴關(guān)系分為寬依賴和窄依賴兩種類型:
窄依賴(Narrow Dependency):指一個(gè)RDD的每個(gè)分區(qū)只依賴于父RDD的一個(gè)或多個(gè)分區(qū),父RDD的每個(gè)分區(qū)最多只被一個(gè)子RDD的分區(qū)使用。窄依賴的特點(diǎn)是數(shù)據(jù)局部性高,可以在同一個(gè)節(jié)點(diǎn)上完成計(jì)算,從而提高計(jì)算效率。
寬依賴(Wide Dependency):指一個(gè)RDD的一個(gè)或多個(gè)分區(qū)依賴于父RDD的多個(gè)分區(qū),或者父RDD的同一個(gè)分區(qū)被多個(gè)子RDD的分區(qū)使用。寬依賴的特點(diǎn)是數(shù)據(jù)局部性較低,需要進(jìn)行數(shù)據(jù)的洗牌操作(Shuffle),從而增加了計(jì)算成本和網(wǎng)絡(luò)傳輸開銷。
在Spark中,每個(gè)寬依賴和窄依賴之間的轉(zhuǎn)換都會形成一個(gè)Stage,每個(gè)Stage包含一組具有相同依賴關(guān)系的Task。一個(gè)Stage中的Task個(gè)數(shù)由多個(gè)因素決定,包括可用的CPU核心數(shù)、可用內(nèi)存大小、數(shù)據(jù)分區(qū)數(shù)等。具體來說,Spark會將RDD劃分成多個(gè)分區(qū),并在每個(gè)分區(qū)上執(zhí)行一個(gè)Task,以便實(shí)現(xiàn)并行計(jì)算。Task的個(gè)數(shù)通常等于RDD的分區(qū)數(shù),這樣可以確保所有Task都具有相同的計(jì)算量,并且可以在不同的節(jié)點(diǎn)上并行執(zhí)行。
在Spark中,Stage劃分的基本原則是:如果兩個(gè)RDD之間存在寬依賴,那么它們就屬于不同的Stage。這是因?yàn)閷捯蕾囆枰M(jìn)行Shuffle操作,需要將數(shù)據(jù)從多個(gè)節(jié)點(diǎn)收集到一個(gè)節(jié)點(diǎn)上進(jìn)行計(jì)算,這會產(chǎn)生較大的網(wǎng)絡(luò)開銷和計(jì)算成本。因此,將寬依賴放在不同的Stage中可以提高計(jì)算效率。而對于窄依賴,Spark會盡量將它們放在同一個(gè)Stage中,以便在同一個(gè)節(jié)點(diǎn)上執(zhí)行計(jì)算,從而提高計(jì)算效率。
7列舉Spark常用的transformation和action算子,有哪些算子會導(dǎo)致Shuffle?
Spark中常用的transformation算子有:
map:對RDD中的每個(gè)元素應(yīng)用一個(gè)函數(shù),返回一個(gè)新的RDD。
filter:對RDD中的每個(gè)元素應(yīng)用一個(gè)謂詞函數(shù),返回一個(gè)包含滿足謂詞的元素的新RDD。
flatMap:類似于map,但是每個(gè)輸入元素可以映射到多個(gè)輸出元素,返回一個(gè)新的RDD。
groupByKey:將具有相同key的元素進(jìn)行分組,返回一個(gè)(key, values)的Tuple,其中values是一個(gè)迭代器。
reduceByKey:將具有相同key的元素進(jìn)行分組,并將每個(gè)key對應(yīng)的values應(yīng)用一個(gè)reduce函數(shù),返回一個(gè)(key, reduced value)的Tuple。
join:對兩個(gè)具有相同key的RDD進(jìn)行join操作,返回一個(gè)新的RDD。
常用的action算子有:
count:返回RDD中元素的個(gè)數(shù)。
collect:將RDD中的所有元素收集到Driver節(jié)點(diǎn)上,并返回一個(gè)數(shù)組。
first:返回RDD中的第一個(gè)元素。
take:返回RDD中前n個(gè)元素。
reduce:對RDD中的元素應(yīng)用一個(gè)reduce函數(shù),返回一個(gè)單個(gè)值。
上述算子中,groupByKey、reduceByKey、join等算子會導(dǎo)致Shuffle操作,因?yàn)樗鼈冃枰獙⒕哂邢嗤琸ey的元素進(jìn)行分組,而這些元素通常分布在不同的節(jié)點(diǎn)上。Shuffle操作會將數(shù)據(jù)從不同的節(jié)點(diǎn)收集到一個(gè)節(jié)點(diǎn)上進(jìn)行計(jì)算,因此需要消耗大量的網(wǎng)絡(luò)和計(jì)算資源。
join()和cogroup():這兩個(gè)算子需要將具有相同鍵的元素進(jìn)行連接操作,也需要進(jìn)行Shuffle操作。
sortByKey():這個(gè)算子需要對RDD中的元素進(jìn)行排序,因此需要進(jìn)行Shuffle操作。
repartition()和coalesce():這兩個(gè)算子都需要對RDD進(jìn)行重新分區(qū)操作,需要進(jìn)行Shuffle操作。
8reduceByKey與groupByKey的區(qū)別,哪一種更具優(yōu)勢?
reduceByKey:按照key進(jìn)行聚合,在shuffle之前有combine(預(yù)聚合)操作,返回結(jié)果是RDD[k,v]。
groupByKey:按照key進(jìn)行分組,直接進(jìn)行shuffle
所以,在實(shí)際開發(fā)過程中,reduceByKey比groupByKey,更建議使用。但是需要注意是否會影響業(yè)務(wù)邏輯。
9Repartition和Coalesce 的關(guān)系與區(qū)別,能簡單說說嗎?
1)關(guān)系:
兩者都是用來改變RDD的partition數(shù)量的,repartition底層調(diào)用的就是coalesce方法:coalesce(numPartitions, shuffle = true)
2)區(qū)別:
repartition一定會發(fā)生shuffle,coalesce 根據(jù)傳入的參數(shù)來判斷是否發(fā)生shuffle。
一般情況下增大rdd的partition數(shù)量使用repartition,減少partition數(shù)量時(shí)使用coalesce。
10簡述下Spark中的緩存(cache和persist)與checkpoint機(jī)制,并指出兩者的區(qū)別和聯(lián)系
關(guān)于Spark緩存和檢查點(diǎn)的區(qū)別,大致可以從這3個(gè)角度去回答:
位置
Persist 和 Cache將數(shù)據(jù)保存在內(nèi)存,Checkpoint將數(shù)據(jù)保存在HDFS
生命周期
Persist 和 Cache 程序結(jié)束后會被清除或手動(dòng)調(diào)用unpersist方法,Checkpoint永久存儲不會被刪除。
RDD依賴關(guān)系
Persist 和 Cache,不會丟掉RDD間的依賴鏈/依賴關(guān)系,CheckPoint會斬?cái)嘁蕾囨湣?br>11簡述Spark中共享變量(廣播變量和累加器)的基本原理與用途
關(guān)于Spark中的廣播變量和累加器的基本原理和用途,
累加器(accumulator)是Spark中提供的一種分布式的變量機(jī)制,其原理類似于mapreduce,即分布式的改變,然后聚合這些改變。累加器的一個(gè)常見用途是在調(diào)試時(shí)對作業(yè)執(zhí)行過程中的事件進(jìn)行計(jì)數(shù)。
廣播變量是在每個(gè)機(jī)器上緩存一份,不可變,只讀的,相同的變量,該節(jié)點(diǎn)每個(gè)任務(wù)都能訪問,起到節(jié)省資源和優(yōu)化的作用。它通常用來高效分發(fā)較大的對象。
12當(dāng)Spark涉及到數(shù)據(jù)庫的操作時(shí),如何減少Spark運(yùn)行中的數(shù)據(jù)庫連接數(shù)?
嗯,有點(diǎn)“調(diào)優(yōu)”的味道,感覺真正的“風(fēng)暴”即將到來,這道題還是很好回答的,我們只需要減少連接數(shù)據(jù)庫的次數(shù)即可。
使用foreachPartition代替foreach,在foreachPartition內(nèi)獲取數(shù)據(jù)庫的連接。
13能介紹下你所知道和使用過的Spark調(diào)優(yōu)嗎?
資源參數(shù)調(diào)優(yōu)
num-executors:設(shè)置Spark作業(yè)總共要用多少個(gè)Executor進(jìn)程來執(zhí)行
executor-memory:設(shè)置每個(gè)Executor進(jìn)程的內(nèi)存
executor-cores:設(shè)置每個(gè)Executor進(jìn)程的CPU core數(shù)量
driver-memory:設(shè)置Driver進(jìn)程的內(nèi)存
spark.default.parallelism:設(shè)置每個(gè)stage的默認(rèn)task數(shù)量
開發(fā)調(diào)優(yōu)
避免創(chuàng)建重復(fù)的RDD
盡可能復(fù)用同一個(gè)RDD
對多次使用的RDD進(jìn)行持久化
盡量避免使用shuffle類算子
使用map-side預(yù)聚合的shuffle操作
使用高性能的算子
①使用reduceByKey/aggregateByKey替代groupByKey
②使用mapPartitions替代普通map
③使用foreachPartitions替代foreach
④使用filter之后進(jìn)行coalesce操作
⑤使用repartitionAndSortWithinPartitions替代repartition與sort類操作
廣播大變量
在算子函數(shù)中使用到外部變量時(shí),默認(rèn)情況下,Spark會將該變量復(fù)制多個(gè)副本,通過網(wǎng)絡(luò)傳輸?shù)絫ask中,此時(shí)每個(gè)task都有一個(gè)變量副本。如果變量本身比較大的話(比如100M,甚至1G),那么大量的變量副本在網(wǎng)絡(luò)中傳輸?shù)男阅荛_銷,以及在各個(gè)節(jié)點(diǎn)的Executor中占用過多內(nèi)存導(dǎo)致的頻繁GC(垃圾回收),都會極大地影響性能。
使用Kryo優(yōu)化序列化性能
優(yōu)化數(shù)據(jù)結(jié)構(gòu)
在可能以及合適的情況下,使用占用內(nèi)存較少的數(shù)據(jù)結(jié)構(gòu),但是前提是要保證代碼的可維護(hù)性。
14如何使用Spark實(shí)現(xiàn)TopN的獲?。枋鏊悸坊蚴褂脗未a)?
使用Spark實(shí)現(xiàn)TopN的一般思路是先使用MapReduce或者Spark計(jì)算出各個(gè)數(shù)據(jù)的得分(或者其他排序依據(jù)),然后再對這些得分進(jìn)行排序,最后取出前N個(gè)得分最高的數(shù)據(jù)。其中,排序的過程是需要進(jìn)行全局操作的,會產(chǎn)生Shuffle操作,因此在性能上需要考慮。
以下是一種使用Spark進(jìn)行TopN操作的偽代碼:
讀取數(shù)據(jù)并將數(shù)據(jù)轉(zhuǎn)換為RDD格式 rdd = sc.textFile("path/to/data").map(parse_data)
計(jì)算每個(gè)數(shù)據(jù)的得分 scores_rdd = rdd.map(lambda data: (data, compute_score(data)))
對得分進(jìn)行排序 sorted_scores_rdd = scores_rdd.sortBy(lambda score: score[1], ascending=False)
取出前N個(gè)得分最高的數(shù)據(jù) topN_rdd = sorted_scores_rdd.take(N)
其中,parse_data函數(shù)用于將原始數(shù)據(jù)解析成程序中需要的格式,compute_score函數(shù)用于計(jì)算數(shù)據(jù)的得分。在第二步和第三步中,需要根據(jù)實(shí)際情況選擇合適的算子,如map()、reduceByKey()、sortBy()等。
作者: 薛秋艷
歡迎關(guān)注微信公眾號 :大數(shù)據(jù)球球