我們?cè)趯W(xué)習(xí)Spark的時(shí)候,到底在學(xué)習(xí)什么?
我必須要說,Spark這個(gè)框架出現(xiàn)之前,我對(duì)很多大數(shù)據(jù)領(lǐng)域的框架源碼甚至都是嗤之以鼻的。
很多小伙伴在群里或者私信留言問我關(guān)于Spark的學(xué)習(xí)路徑問題。
Spark發(fā)展至今,應(yīng)該說已經(jīng)非常成熟了。是大數(shù)據(jù)計(jì)算領(lǐng)域不得不學(xué)習(xí)的框架。尤其是Spark在穩(wěn)定性和社區(qū)發(fā)展的成熟度方面,基本可以吊打其他的大數(shù)據(jù)處理框架。
我之前發(fā)過一篇關(guān)于閱讀Spark源碼的文章:《Spark源碼閱讀的正確打開方式》。
我們?cè)谶@篇文章的基礎(chǔ)上總結(jié)一下我曾經(jīng)總結(jié)過的關(guān)于Spark的路徑。如果有什么更好的資料,歡迎大家加我微信推薦給我。
Spark的背景和核心論文
假如你是第一次接觸Spark,那么你需要對(duì)Spark的設(shè)計(jì)思想有所了解,知道Spark用了哪些抽象,Spark在提出RDD的時(shí)候是基于什么樣的考慮。
在這里給大家推薦幾篇論文如下:
第一篇:《彈性分布式數(shù)據(jù)集:一種為內(nèi)存化集群計(jì)算設(shè)計(jì)的容錯(cuò)抽象》,鏈接如下:
https://fasionchan.com/blog/2017/10/19/yi-wen-tan-xing-fen-bu-shi-shu-ju-ji-yi-zhong-wei-nei-cun-hua-ji-qun-ji-suan-she-ji-de-rong-cuo-mo-xing/
這篇文章中提出了彈性分布式數(shù)據(jù)集(RDD,Resilient Distributed Datasets)這個(gè)概念,這個(gè)概念是貫穿Spark設(shè)計(jì)的始終,是Spark最重要的概念之一。RDD是一種分布式的內(nèi)存抽象,允許在大型集群上執(zhí)行基于內(nèi)存的計(jì)算(In-Memory Computing),與此同時(shí)還保持了MapReduce等數(shù)據(jù)流模型的容錯(cuò)特性。
這篇文章中提到,Spark實(shí)現(xiàn)RDD在迭代計(jì)算方面比Hadoop快二十多倍,同時(shí)還可以在5-7秒的延時(shí)內(nèi)交互式地查詢1TB的數(shù)據(jù)集。
- 第二篇:《大型集群上的快速和通用數(shù)據(jù)處理架構(gòu)》
這本書我不給連接了。因?yàn)檫@個(gè)文章長(zhǎng)達(dá)170多頁,堪比一篇博士論文。相信絕大多數(shù)人都是沒興趣讀完的。
我在這里給出一個(gè)讀后小總結(jié):
這本書是Spark框架設(shè)計(jì)者–計(jì)算機(jī)科學(xué)博士Matei Alexandru Zaharia和加州大學(xué)伯克利分校教授、主席Scott Shenker撰寫的。書中作者主要分析了當(dāng)前流行的各種計(jì)算框架的使用場(chǎng)景以及他們對(duì)應(yīng)的缺點(diǎn),然后談了下為什么編寫了Spark這個(gè)框架和spark每個(gè)模塊詳細(xì)的設(shè)計(jì)理念及運(yùn)行原理,這里是做一部分摘要。
隨著現(xiàn)在需要處理的數(shù)據(jù)量越來越大,單機(jī)處理要向集群進(jìn)行擴(kuò)展,這就會(huì)帶來三個(gè)集群維度上的問題
1)并行化:多個(gè)節(jié)點(diǎn)同時(shí)進(jìn)行數(shù)據(jù)處理
2)容錯(cuò):在多節(jié)點(diǎn)上處理數(shù)據(jù),節(jié)點(diǎn)的故障和慢節(jié)點(diǎn)會(huì)變得非常常見
3)資源的動(dòng)態(tài)分配:一般集群都是在多個(gè)用戶之前進(jìn)行切換,所以資源的動(dòng)態(tài)擴(kuò)展和縮減就變得非常重要
和MapReduce對(duì)比 MapReduce做為計(jì)算引擎與Spark的區(qū)別在于:Spark RDD在并行計(jì)算階段之間能夠高效的共享數(shù)據(jù)。MapReduce計(jì)算模型中,map結(jié)果必須要從內(nèi)存落到磁盤,然后reduce再將數(shù)據(jù)加載到內(nèi)存中,得到的結(jié)果再次落到磁盤中;如果是多個(gè)MapReduce操作數(shù)據(jù),那么reduce結(jié)果數(shù)據(jù)還要再次加載到下一個(gè)map內(nèi)存。正是由于數(shù)據(jù)一次次從磁盤加載到內(nèi)存,所以MapReduce才會(huì)異常的慢。這也是Spark和MapReduce的區(qū)別,Spark RDD能夠?qū)?shù)據(jù)cache到內(nèi)存中,省去了從磁盤加載的過程,同時(shí)Spark shuffle過程中的數(shù)據(jù)也是直接放在內(nèi)存中的(為了避免shuffle失敗map數(shù)據(jù)丟失Spark框架還對(duì)shuffle進(jìn)行了checkpoint),這就是為什么spark比MapReduce塊的原因。Spark解決的核心問題也就是數(shù)據(jù)流模型在計(jì)算過程中高效的共享數(shù)據(jù) 。RDD具有可容錯(cuò)和并行數(shù)據(jù)結(jié)構(gòu)特征,這使得用戶可以指定數(shù)據(jù)存儲(chǔ)到硬盤還是內(nèi)存、控制數(shù)據(jù)的分區(qū)方法并在數(shù)據(jù)集上進(jìn)行種類豐富的操作。
容錯(cuò) 一般的框架有兩種容錯(cuò)方式,提供容錯(cuò)性的方法就要么是在主機(jī)之間復(fù)制數(shù)據(jù),要么對(duì)各主機(jī)的更新情況做日志記錄。
第一種容錯(cuò)的方式恢復(fù)時(shí)間短但需要消耗更多的內(nèi)存和磁盤空間用來存儲(chǔ)數(shù)據(jù)。
第二種方式不需要額外內(nèi)存但是恢復(fù)時(shí)間比較長(zhǎng)。這兩種方法對(duì)于數(shù)據(jù)密集型的任務(wù)來說代價(jià)很高,因?yàn)樗鼈冃枰趲掃h(yuǎn)低于內(nèi)存的集群網(wǎng)絡(luò)間拷貝大量的數(shù)據(jù),同時(shí)還將產(chǎn)生大量的存儲(chǔ)開銷。與上述系統(tǒng)不同的是,RDD提供一種基于粗粒度變換(如, map, filter, join)的接口,該接口會(huì)將相同的操作應(yīng)用到多個(gè)數(shù)據(jù)集上。這使得他們可以通過記錄用來創(chuàng)建數(shù)據(jù)集的變換(lineage),而不需存儲(chǔ)真正的數(shù)據(jù),進(jìn)而達(dá)到高效的容錯(cuò)性。當(dāng)一個(gè)RDD的某個(gè)分區(qū)丟失的時(shí)候,RDD記錄有足夠的信息記錄其如何通過其他的RDD進(jìn)行計(jì)算,且只需重新計(jì)算該分區(qū)。因此,丟失的數(shù)據(jù)可以被很快的恢復(fù),而不需要昂貴的復(fù)制代價(jià)。
RDD RDD是一個(gè)分區(qū)的只讀記錄的集合,用戶可以控制RDD的其他兩個(gè)方面:持久化和分區(qū)。用戶可以選擇重用哪個(gè)RDD,并為其制定存儲(chǔ)策略(比如,內(nèi)存存儲(chǔ)),也可以讓RDD中的數(shù)據(jù)根據(jù)記錄的key分布到集群的多個(gè)機(jī)器,這對(duì)位置優(yōu)化來說是有用的,比如可用來保證兩個(gè)要Jion的數(shù)據(jù)集都使用了相同的哈希分區(qū)方式。默認(rèn)情況下,Spark會(huì)將調(diào)用過persist的RDD存在內(nèi)存中。但若內(nèi)存不足,也可以將其寫入到硬盤上。通過指定persist函數(shù)中的參數(shù),用戶也可以請(qǐng)求其他持久化策略并通過標(biāo)記來進(jìn)行persist,比如僅存儲(chǔ)到硬盤上,又或是在各機(jī)器之間復(fù)制一份。最后,用戶可以在每個(gè)RDD上設(shè)定一個(gè)持久化的優(yōu)先級(jí)來指定內(nèi)存中的哪些數(shù)據(jù)應(yīng)該被優(yōu)先寫入到磁盤。RDD的第一個(gè)優(yōu)點(diǎn)是可以使用lineage恢復(fù)數(shù)據(jù),不需要檢查點(diǎn)的開銷,此外,當(dāng)出現(xiàn)失敗時(shí),RDDs的分區(qū)中只有丟失的那部分需要重新計(jì)算,而且該計(jì)算可在多個(gè)節(jié)點(diǎn)上并發(fā)完成,不必回滾整個(gè)程序 RDD的第二個(gè)優(yōu)點(diǎn)是,不可變性讓系統(tǒng)像MapReduce那樣用后備任務(wù)代替運(yùn)行緩慢的任務(wù)來減少緩慢節(jié)點(diǎn) (stragglers) 的影響 在RDDs上的批量操作過程中,任務(wù)的執(zhí)行可以根據(jù)數(shù)據(jù)的所處的位置來進(jìn)行優(yōu)化,從而提高性能,其次,只要所進(jìn)行的操作是只基于掃描的,當(dāng)內(nèi)存不足時(shí),RDD的性能下降也是平穩(wěn)的。不能載入內(nèi)存的分區(qū)可以存儲(chǔ)在磁盤上,其性能也會(huì)與當(dāng)前其他數(shù)據(jù)并行系統(tǒng)相當(dāng)。RDDS最適合對(duì)數(shù)據(jù)集中所有的元素進(jìn)行相同的操作的批處理類應(yīng)用。RDDS不太適用于通過異步細(xì)粒度更新來共享狀態(tài)的應(yīng)用,比如針對(duì)Web應(yīng)用或增量網(wǎng)絡(luò)爬蟲的存儲(chǔ)系統(tǒng)
寬窄依賴 窄依賴允許在單個(gè)集群節(jié)點(diǎn)上流水線式執(zhí)行,這個(gè)節(jié)點(diǎn)可以計(jì)算所有父級(jí)分區(qū) 。相反,寬依賴需要所有的父RDD數(shù)據(jù)可用并且數(shù)據(jù)已經(jīng)通過類MapReduce的操作shuffle完成 其次,在窄依賴中,節(jié)點(diǎn)失敗后的恢復(fù)更加高效。因?yàn)橹挥衼G失的父級(jí)分區(qū)需要重新計(jì)算,并且這些丟失的父級(jí)分區(qū)可以并行地在不同節(jié)點(diǎn)上重新計(jì)算。與此相反,在寬依賴的繼承關(guān)系中,單個(gè)失敗的節(jié)點(diǎn)可能導(dǎo)致一個(gè)RDD的所有先祖RDD中的一些分區(qū)丟失,導(dǎo)致計(jì)算的重新執(zhí)行。
Spark的調(diào)度器會(huì)額外考慮被持久化(persist)的RDD的那個(gè)分區(qū)保存在內(nèi)存中并可供使用,當(dāng)用戶對(duì)一個(gè)RDD執(zhí)行Action(如count 或save)操作時(shí),調(diào)度器會(huì)根據(jù)該RDD的lineage,來構(gòu)建一個(gè)由若干 階段(stage) 組成的一個(gè)DAG(有向無環(huán)圖)以執(zhí)行程序,每個(gè)stage都包含盡可能多的連續(xù)的窄依賴型轉(zhuǎn)換。各個(gè)階段之間的分界則是寬依賴所需的shuffle操作,或者是DAG中一個(gè)經(jīng)由該分區(qū)能更快到達(dá)父RDD的已計(jì)算分區(qū)。之后,調(diào)度器運(yùn)行多個(gè)任務(wù)來計(jì)算各個(gè)階段所缺失的分區(qū),直到最終得出目標(biāo)RDD。調(diào)度器向各機(jī)器的任務(wù)分配采用延時(shí)調(diào)度機(jī)制并根據(jù)數(shù)據(jù)存儲(chǔ)位置(本地性)來確定。若一個(gè)任務(wù)需要處理的某個(gè)分區(qū)剛好存儲(chǔ)在某個(gè)節(jié)點(diǎn)的內(nèi)存中,則該任務(wù)會(huì)分配給那個(gè)節(jié)點(diǎn)。否則,如果一個(gè)任務(wù)處理的某個(gè)分區(qū),該分區(qū)含有的RDD提供較佳的位置(例如,一個(gè)HDFS文件),我們把該任務(wù)分配到這些位置。對(duì)應(yīng)寬依賴類的操作 {比如w shuffle依賴),我們會(huì)將中間記錄物理化到保存父分區(qū)的節(jié)點(diǎn)上。這和MapReduce物化Map的輸出類似,能簡(jiǎn)化數(shù)據(jù)的故障恢復(fù)過程 對(duì)于執(zhí)行失敗的任務(wù),只要它對(duì)應(yīng)stage的父類信息仍然可用,它便會(huì)在其他節(jié)點(diǎn)上重新執(zhí)行。如果某些stage變?yōu)椴豢捎茫ɡ?,因?yàn)閟huffle在map階段的某個(gè)輸出丟失了),則重新提交相應(yīng)的任務(wù)以并行計(jì)算丟失的分區(qū)。(DAGscheduler官方定義) 若某個(gè)任務(wù)執(zhí)行緩慢 (即"落后者"straggler),系統(tǒng)則會(huì)在其他節(jié)點(diǎn)上執(zhí)行該任務(wù)的拷貝。這與MapReduce做法類似,并取最先得到的結(jié)果作為最終的結(jié)果。
Spark內(nèi)存管理 Spark提供了三種對(duì)持久化RDD的存儲(chǔ)策略:未序列化Java對(duì)象存于內(nèi)存中、序列化后的數(shù)據(jù)存于內(nèi)存及磁盤存儲(chǔ)。第一個(gè)選項(xiàng)的性能表現(xiàn)是最優(yōu)秀的,因?yàn)榭梢灾苯釉L問在JAVA虛擬機(jī)內(nèi)存里的RDD對(duì)象。在空間有限的情況下,第二種方式可以讓用戶采用比JAVA對(duì)象圖更有效的內(nèi)存組織方式,代價(jià)是降低了性能。第三種策略適用于RDD太大難以存儲(chǔ)在內(nèi)存的情形,但每次重新計(jì)算該RDD會(huì)帶來額外的資源開銷。
對(duì)于有限可用內(nèi)存,我們使用以RDD為對(duì)象的LRU(最近最少使用)回收算法來進(jìn)行管理。當(dāng)計(jì)算得到一個(gè)新的RDD分區(qū),但卻沒有足夠空間來存儲(chǔ)它時(shí),系統(tǒng)會(huì)從最近最少使用的RDD中回收其一個(gè)分區(qū)的空間。
除非該RDD便是新分區(qū)對(duì)應(yīng)的RDD,這種情況下,Spark會(huì)將舊的分區(qū)繼續(xù)保留在內(nèi)存,防止同一個(gè)RDD的分區(qū)被循環(huán)調(diào)入調(diào)出。這點(diǎn)很關(guān)鍵–因?yàn)榇蟛糠值牟僮鲿?huì)在一個(gè)RDD的所有分區(qū)上進(jìn)行,那么很有可能已經(jīng)存在內(nèi)存中的分區(qū)將會(huì)被再次使用。到目前為止,這種默認(rèn)的策略在我們所有的應(yīng)用中都運(yùn)行很好, 當(dāng)然我們也為用戶提供了“持久化優(yōu)先級(jí)”選項(xiàng)來控制RDD的存儲(chǔ)。
大家可以看到,這7個(gè)概念都是Spark中最最核心的幾個(gè)概念。我們?cè)趯W(xué)習(xí)過程中是萬萬繞不過去的。
模塊拆分&學(xué)習(xí)


第一張圖是官方給出的Spark架構(gòu)圖,我們可以看到幾個(gè)最重要的模塊:Spark Core、Spark Streaming、Spark SQL。曾經(jīng)還有一個(gè)部分叫做Structured Streaming,但是這部分好像慢慢被官方拋棄了,現(xiàn)在Spark官方主推SQL并且基于Spark SQL進(jìn)行的優(yōu)化和迭代非常之多。如果你是第一次接觸Spark,并且業(yè)務(wù)沒有特殊需要,可以暫時(shí)忽略Structured Streaming。此外Spark社區(qū)在努力的像機(jī)器學(xué)習(xí)和深度學(xué)習(xí)靠攏,Spark在完成最初的流計(jì)算目標(biāo)后開始發(fā)力機(jī)器學(xué)習(xí)方向,如果有興趣可以接觸這一部分的內(nèi)容。
第二張圖是一個(gè)簡(jiǎn)單的Spark快速學(xué)習(xí)的路線圖,一些基本的Linux操作和運(yùn)維基礎(chǔ),一點(diǎn)簡(jiǎn)單的搭建虛擬機(jī)的基礎(chǔ),我相信這些對(duì)大家來說都不是問題。然后我們就可以按照官網(wǎng)的demo進(jìn)行第一次體驗(yàn)了:http://spark.apache.org/examples.html
Spark的官網(wǎng)中給出了非常簡(jiǎn)單的Spark入門案例,同樣我們也可以直接訪問Spark在Github的倉(cāng)庫(kù)直接看更多的Demo:
https://github.com/apache/spark/tree/master/examples/src/main/java/org/apache/spark/examples
書推薦
關(guān)于Spark的書,我個(gè)人讀了應(yīng)該有4-5本,每本書都沒有達(dá)到我的預(yù)期,如果說你真的需要一本書來當(dāng)成工具,我覺得下面的書和Github項(xiàng)目可以用來參考:
第一本書是:《大數(shù)據(jù)處理框架Apache Spark設(shè)計(jì)與實(shí)現(xiàn)》,這本書主要是介紹Spark的設(shè)計(jì)和原理,包含一部分源碼。你可以把它當(dāng)成一本八股文書來背,當(dāng)然也可以當(dāng)成一本指南來深入理解Spark的設(shè)計(jì)理念和深層次的原理。
這本書對(duì)應(yīng)了一個(gè)Github的Repo:
https://github.com/wangzhiwubigdata/SparkInternals

還有一本電子書:http://marsishandsome.github.io/SparkSQL-Internal/
是關(guān)于Spark SQL的,這本書寫的可謂用心良苦。對(duì)SparkSQL的發(fā)展歷程和性能的優(yōu)化、SparkSQL的使用方法、調(diào)優(yōu)、架構(gòu)、優(yōu)化器Catalyst以及其他的各個(gè)模塊都有詳細(xì)介紹。
當(dāng)然我也寫過一些關(guān)于Spark SQL的經(jīng)典文章:
《SparkSQL 整體運(yùn)行架構(gòu)和底層實(shí)現(xiàn)
《Spark SQL重點(diǎn)知識(shí)總結(jié)
《關(guān)于SparkSQL的開窗函數(shù),你應(yīng)該知道這些!
《SparkSQL的3種Join實(shí)現(xiàn)?
《真·屠龍之術(shù) | 一次SparkSQL性能分析與優(yōu)化之旅及相關(guān)工具小結(jié)》
《SparkSQL的自適應(yīng)執(zhí)行-Adaptive Execution》
Github推薦
除了上面的推薦書對(duì)應(yīng)的repo,還有一個(gè)酷玩Spark:
https://github.com/wangzhiwubigdata/CoolplaySpark
這個(gè)倉(cāng)庫(kù)是由騰訊廣告部的同學(xué)發(fā)起的,主要是Spark 源代碼解析、Spark 類庫(kù)等,源代碼部分對(duì)Spark Streaming 和 Structured Streaming部分由非常深入的解釋。但是這個(gè)倉(cāng)庫(kù)最后一次維護(hù)已經(jīng)是2019年五月份。大家都知道2019年底Flink開源,可能搶了一部分熱度,很多公司都開始轉(zhuǎn)向?qū)link的研究。
源碼閱讀
Spark至今只經(jīng)歷過1.x、2.x和3.x三個(gè)大版本的變化,在核心實(shí)現(xiàn)上,我們?cè)贕ithub能看到的最早的實(shí)現(xiàn)是0.5版本,這個(gè)版本只有1萬多行代碼,就把Spark的核心功能實(shí)現(xiàn)了。
當(dāng)然我們不可能從這么古老的版本看,假如你接觸過Spark,現(xiàn)在準(zhǔn)備看源碼,那么我建議從2.x版本中選取一個(gè),最好是2.3或者2.4。但是經(jīng)過如此多的迭代,Spark的代碼量已經(jīng)暴增了幾倍。關(guān)于Spark3.x中的新增功能和優(yōu)化例如動(dòng)態(tài)資源分配,可以針對(duì)性的進(jìn)行補(bǔ)充即可。
我把最重要的模塊列表如下:
Spark的初始化
SparkContext SparkEnv SparkConf RpcEnv SparkStatusTracker SecurityManager SparkUI MetricsSystem TaskScheduler
Spark的存儲(chǔ)體系
SerializerManager BroadcastManager ShuffleManager MemoryManager NettyBlockTransferService BlockManagerMaster BlockManager CacheManager
Spark的內(nèi)存管理
MemoryManager MemoryPool ExecutionMemoryPool StorageMemoryPool MemoryStore UnifiedMemoryManager
Spark的運(yùn)算體系
LiveListenerBus MapOutputTracker DAGScheduler TaskScheduler ExecutorAllocationManager OutputCommitCoordinator ContextClearner
Spark的部署模式
LocalSparkCluster Standalone Mater/Executor/Worker的容錯(cuò)
Spark Streaming
StreamingContext Receiver Dstream 窗口操作
Spark SQL
Catalog TreeNode 詞法解析器Parser RuleExecutor Analyzer與Optimizer HiveSQL相關(guān)
其他
假如你對(duì)圖計(jì)算Spark GraphX和機(jī)器學(xué)習(xí)Spark MLlib感興趣,可以單獨(dú)看看。
一些可以直接入門的項(xiàng)目
我曾經(jīng)發(fā)過一些可以直接入門的項(xiàng)目,大家可以參考:
這里就不得不說B站了,你可以在B站找到非常豐富的學(xué)習(xí)資源,甚至我自己也曾經(jīng)上傳過關(guān)于Spark的項(xiàng)目。
我這里找了一個(gè)不錯(cuò)的入門視頻:https://www.bilibili.com/video/BV1tp4y1B7qd
另外下面這篇文章也是一個(gè)完整的入門案例:
《Spark Streaming + Canal + Kafka打造Mysql增量數(shù)據(jù)實(shí)時(shí)進(jìn)行監(jiān)測(cè)分析》
另外,給自己打個(gè)廣告,我個(gè)人從一個(gè)弱雞資源UP主正式開始自己錄制視頻,要當(dāng)一個(gè)合格的大數(shù)據(jù)領(lǐng)域硬核原創(chuàng)作者。主要專注關(guān)于面試&學(xué)習(xí)路線&個(gè)人成長(zhǎng)&職場(chǎng)進(jìn)階。歡迎各位大大關(guān)注:
調(diào)優(yōu)和面試
好了,這部分就是我個(gè)人曾經(jīng)發(fā)過的文章總結(jié)了,大家面試不會(huì)吃虧的:
【大數(shù)據(jù)嗶嗶集】Spark面試題靈魂40問
《Spark Streaming性能優(yōu)化: 如何在生產(chǎn)環(huán)境下動(dòng)態(tài)應(yīng)對(duì)流數(shù)據(jù)峰值》
Spark數(shù)據(jù)傾斜問題解決方案全面總結(jié)
Spark調(diào)優(yōu) | Spark SQL參數(shù)調(diào)優(yōu)
三萬字長(zhǎng)文 | Spark性能優(yōu)化實(shí)戰(zhàn)手冊(cè)
Spark面對(duì)OOM問題的解決方法及優(yōu)化總結(jié)
好了,本次分享就到這里了。歡迎大家「分享」和「在看」。
我是王知無,一個(gè)大數(shù)據(jù)領(lǐng)域的硬核原創(chuàng)作者,關(guān)注技術(shù)提升&個(gè)人成長(zhǎng)&職場(chǎng)進(jìn)階,歡迎關(guān)注。
