為什么Spark能成為最火的大數據計算引擎?它是怎樣工作的?

本文轉載自微信公眾號「大數據DT(ID:hzdashuju)」,作者朱凱。轉載本文請聯系大數據DT公眾號。
01 概述
十年前我們只有Hadoop,大家首先通過HDFS實現海量數據的共享存儲,然后使用MapReduce以批處理的方式處理這些海量數據,這一切看起來似乎十分完美。
但眾口難調啊,有人覺得MapReduce的編程模型太難使用了,為什么不能使用SQL來分析數據呢?我們數據庫領域已經有非常成熟的數據倉庫模型了,為何不實現一個大數據技術的數據倉庫呢?于是Hive類的框架便誕生了,人們開始使用Hive類的框架來構建大數據技術的數據倉庫,使用SQL查詢數據。
接著人們又開始詬病MapReduce的執行效率太慢,因為它本質上是面向批處理場景的,難以支撐一些實時性要求很高的場景,我們需要一種能夠支撐流計算的架構,于是Storm類的框架誕生了。人們開始使用Storm這類框架處理流計算場景。
接著伴隨垃圾郵件分析、商品推薦、金融風控這類應用場景需求的出現,又迫使我們需要在大數據場景下具備機器學習的能力,于是乎Mahout類的框架出現了,人們使用它們來進行大數據下的機器學習。
隨著越來越多來自應用領域的細分需求,人們從最初Hadoop的HDFS和MapReduce開始,一步步地構造出了各種細分領域的技術框架。有專攻處理批處理場景的,有專攻數據倉庫場景的,有處理流計算場景的,也有專職機器學習的。
在我看來這有點像在給Hadoop打補丁,因為Hadoop在設計之初根本沒有考慮過這么多的場景,它只是為了支撐離線批處理。但是需求擺在這里,為了實現目標只得另起爐灶通過設計一個全新的系統滿足需求。這種現狀造成了很多問題。
- 重復工作:不同的系統之間都需要解決一些相同的共性問題,比如分布式執行和容錯性。例如MapReduce、SQL查詢引擎和機器學習系統都會涉及聚合操作。
- 組合:不同系統之間的組合使用非常“昂貴”,因為不同系統之間無法有效的功效數。為了組合使用我們需要將數據在不同的系統之間頻繁的導出導入,數據用來移動的時間可能都會超過計算的時間。
- 維護成本:雖然這些系統從每個個體的角度來看都十分優秀,但是它們都是在不同時期由不同的團隊設計實現的,其設計思路和實現方式也各不相同。這導致平臺在部署運維這些系統的時候十分痛苦,因為它們差異太大了。
- 學習成本:系統之間巨大的差異性對于開發人員來講更是如此,這些技術框架擁有不同的邏輯對象、專業術語、API和編程模型,每種框架都需要重新學習一遍才能使用。
Spark意識到了這個問題,作為一個后起之秀它擁有天然的優勢。Spark誕生于2012年,那個時候Hadoop生態已經經過了6個年頭的發展,其生態格局已經成型。Spark已經能夠看清大數據有哪些細分領域,同時MapReduce、Hive、Storm等開源組件也已經發展多年,Spark也能夠了解到它們的長處和不足。
于是Spark橫空出世,成為目前開源社區最為火爆的一款分布式內存計算引擎。Spark使用DAG(有向無環圖)模型作為其執行模型,并且主要使用內存計算的方式進行任務計算。
Spark基于一套統一的數據模型(RDD)和編程模型(Trans-foration /Action)之上,構建出了Spark SQL、Spark Streaming、Spark MLibs等多個分支,其功能涵蓋了大數據的多個領域,如圖2-14所示。
▲圖2-14 Spark涵蓋的領域
Spark通過統一的數據模型和編程模型,構造出了SQL查詢、流計算、機器學習和圖計算等多個分支庫。
02 數據模型
RDD是彈性分布式數據集(Resilient Distributed Datasets)的縮寫,它是MapReduce模型的擴展和延伸。Spark之所以能夠同時支撐大數據的多個領域,在很大程度上是依靠了RDD的能力。
雖然批處理、流計算、圖計算和機器學習這些計算場景之間初看起來風馬牛不相及,但是它們都存在一個共同的需求,那就是在并行計算階段能夠高效的共享數據。
RDD的設計者們洞穿了這一現象,于是通過高效的數據共享概念和類似MapReduce的操作設計了RDD,使得它能模擬迭代式算法、關系查詢、MapReduce和流式處理等多種編程模型。
同時它也是一個可容錯的、可并行的數據結構,可以讓用戶指定將數據存儲到磁盤和內存中,并能控制數據的分區。同時它還提供了一些高效的編程接口操作數據集。
03 編程模型和作業調度
Spark將RDD的操作分為兩類:轉換(transformation)與行動(action)。
轉換操作是一種惰性操作,它只會定義新的RDD,而不會立即執行。而行動操作則是立即執行計算,它要么返回結果給Driver進程,或是將結果輸出到外部存儲。常見轉換操作如map、flatMap、filter等,常見行動操作如count、collect等。
當用戶對一個RDD執行了行動操作之后,調度器會根據RDD的依賴關系生成一個DAG(有向無環圖)圖來執行程序。DAG由若干個stage組成,每個stage內都包含多個連續的窄依賴。而各個stage之間則是寬依賴。如圖2-15所示,實線方框代表的是RDD。方框內的矩形代表分區,若分區已在內存中保存則用黑色表示。
▲圖2-15 Spark任務拆分示意
04 依賴
RDD作為數據結構,本質上是一個只讀的分區記錄集合。一個RDD可以包含多個分區,每個分區是一個數據片段。
RDD可以相互依賴。如果父RDD的每個分區最多被一個子RDD的分區使用,則稱之為窄依賴;若多個子RDD分區依賴一個父RDD的分區,則稱之為寬依賴。不同的操作依據其特性,可能會產生不同的依賴。例如map操作會產生窄依賴,而join操作則產生寬依賴。
Spark之所以將依賴分為兩種,基于兩點原因。首先,窄依賴支持在同單個集群上以管道的形式式執,例如在執行了map后,緊接著執行filter。相反,寬依賴需要所有的父RDD數據都可用并通過shuffle動作才可繼續執行。
其次,窄依賴的失敗恢復更加高效,因為它只需要重新計算丟失的父分區,并且這些計算可以并行的在不同節點同時進行。與此相反,在寬依賴的繼承關系中,單個失敗的節點可能導致一個RDD的所有先祖RDD中的一些分區丟失,導致計算的重新執行。如圖2-16所示,說明了窄依賴與寬依賴之間的區別。
▲圖2-16 SparkRDD寬依賴和窄依賴示意
05 容錯
傳統分布式系統的容錯方案有據復制和恢復日志兩種方案。對于以數據為中心的系統而言,這兩種方式都非常昂貴,因為它需要跨集群網絡復制大量數據,而網絡帶寬的速度遠遠低于內存訪問的速度。
RDD天生是支持容錯的。首先,它自身是一個不變的數據集,其次,Spark使用DAG作為其執行模型,所以它能夠通過RDD的依賴特性記住一系列操作生成一張DAG圖。因此當執行的任務失敗時,Spark只需根據DAG圖進行重新計算即可實現容錯機制。由于無須采用復制的方式支持容錯,Spark很好地降低了跨網絡的數據傳輸成本。
06 集群模式
Spark的應用以一組獨立進程的形式運行在一個集群之上,由主程序中的SparkContext對象進行協調(也被稱為driver程序)。Spark目前支持三種集群運行方式。
具體來說,Spark既可以通過standlone模式獨立運行,也可以運行在Mesos或者YARN之上。
如圖2-17所示,一旦SparkContext連接到集群,Spark首先會從集群的節點中獲得一些executor進程,這些進程會用來執行我們程序中的計算和存儲邏輯,接著它會通過jar包的形式分發我們的程序代碼到各個executor進程。最后,SparkContext會分派任務到各executor進程進行執行。
▲圖2-17 Spark任務進程示意
每個應用都擁有自己的executor進程,這些進程會在整個應用生命周期內持續運行并以多線程的方式執行具體的任務。這種設計的好處是將各個應用之間的資源消耗進行了隔離,每個應用都運行在它們各自的JVM中。但是這也意味著不同應用之間的SparkContext無法共享數據,除非借助擴展的存儲媒介。
Spark對底層集群管理不可知。只要能夠獲取到executor進行,并且這些進程之間可以通信,它就能比較容易的運行在其他通用集群資源調度框架之上,如Mesos和YARN。
07 使用場景
Spark借助其RDD的出色設計,做到了橫跨多個領域的支撐。這意味著我們在一套程序邏輯之中可以集成多種操作。
例如使用SQL查詢過濾數據,然后進行機器學習或是通過SQL的方式操作流數據。在提升便利的同時也降低了開發人員的學習曲線,基于Spark,只需要學習一套編程模型即可處理多個領域。
所以將Spark作為平臺的一站式計算解決方案是再合適不過了。
關于作者:朱凱,資深大數據專家和架構師,擁有10年IT從業經驗,精通大數據、Java、Node.JS等技術。對大數據領域的主流技術與解決方案有深入研究,擅長分布式系統的架構設計與整合。曾主導過多款大數據平臺級產品的規劃設計與研發工作,一線實戰經驗豐富。
本文摘編自《企業級大數據平臺構建:架構與實現》,經出版方授權發布。