摘要:相比MapReduce僵化的Map與Reduce分階段計算相比,Spark的計算框架更加富有彈性和靈活性,執行效能更佳。
本文分享自華為雲社群《Spark架構原理》,作者:JavaEdge。
相比MapReduce僵化的Map與Reduce分階段計算相比,Spark的計算框架更加富有彈性和靈活性,執行效能更佳。
Spark的計算階段
- MapReduce一個應用一次只執行一個map和一個reduce
- Spark可根據應用的複雜度,分割成更多的計算階段(stage),組成一個有向無環圖DAG,Spark任務排程器可根據DAG的依賴關係執行計算階段
邏輯迴歸機器學習效能Spark比MapReduce快100多倍。因為某些機器學習演算法可能需要進行大量迭代計算,產生數萬個計算階段,這些計算階段在一個應用中處理完成,而不是像MapReduce那樣需要啟動數萬個應用,因此執行效率極高。
DAG,有向無環圖,不同階段的依賴關係是有向的,計算過程只能沿依賴關係方向執行,被依賴的階段執行完成前,依賴的階段不能開始執行。該依賴關係不能有環形依賴,否則就死迴圈。
典型的Spark執行DAG的不同階段:
整個應用被切分成3個階段,階段3需要依賴階段1、2,階段1、2互不依賴。Spark執行排程時,先執行階段1、2,完成後,再執行階段3。對應Spark虛擬碼:
rddB = rddA.groupBy(key) rddD = rddC.map(func) rddF = rddD.union(rddE) rddG = rddB.join(rddF)
所以Spark作業排程執行的核心是DAG,整個應用被切分成數個階段,每個階段的依賴關係也很清楚。根據每個階段要處理的資料量生成任務集合(TaskSet),每個任務都分配一個任務程式去處理,Spark就實現了大資料的分散式計算。
負責Spark應用DAG生成和管理的元件是DAGScheduler,DAGScheduler根據程式程式碼生成DAG,然後將程式分發到分散式計算叢集,按計算階段的先後關係排程執行。
那麼Spark劃分計算階段的依據是什麼呢?顯然並不是RDD上的每個轉換函式都會生成一個計算階段,比如上面的例子有4個轉換函式,但是隻有3個階段。
你可以再觀察一下上面的DAG圖,關於計算階段的劃分從圖上就能看出規律,當RDD之間的轉換連線線呈現多對多交叉連線的時候,就會產生新的階段。一個RDD代表一個資料集,圖中每個RDD裡面都包含多個小塊,每個小塊代表RDD的一個分片。
一個資料集中的多個資料分片需要進行分割槽傳輸,寫入到另一個資料集的不同分片中,這種資料分割槽交叉傳輸的操作,我們在MapReduce的執行過程中也看到過。
是的,這就是shuffle過程,Spark也需要通過shuffle將資料進行重新組合,相同Key的資料放在一起,進行聚合、關聯等操作,因而每次shuffle都產生新的計算階段。這也是為什麼計算階段會有依賴關係,它需要的資料來源於前面一個或多個計算階段產生的資料,必須等待前面的階段執行完畢才能進行shuffle,並得到資料。
計算階段劃分的依據是shuffle,不是轉換函式的型別,有的函式有時有shuffle,有時沒有。如上圖例子中RDD B和RDD F進行join,得到RDD G,這裡的RDD F需要進行shuffle,RDD B不需要。
因為RDD B在前面一個階段,階段1的shuffle過程中,已進行了資料分割槽。分割槽數目和分割槽K不變,無需再shuffle:
- 這種無需進行shuffle的依賴,在Spark裡稱作窄依賴
- 需要進行shuffle的依賴,被稱作寬依賴
類似MapReduce,shuffle對Spark也很重要,只有通過shuffle,相關資料才能互相計算。
既然都要shuffle,為何Spark就更高效?
本質上看,Spark算是一種MapReduce計算模型的不同實現。Hadoop MapReduce簡單粗暴根據shuffle將大資料計算分成Map、Reduce兩階段就完事。但Spark更細,將前一個的Reduce和後一個的Map連線,當作一個階段持續計算,形成一個更優雅、高效地計算模型,其本質依然是Map、Reduce。但這種多個計算階段依賴執行的方案可有效減少對HDFS的訪問,減少作業的排程執行次數,因此執行速度更快。
不同於Hadoop MapReduce主要使用磁碟儲存shuffle過程中的資料,Spark優先使用記憶體進行資料儲存,包括RDD資料。除非記憶體不夠用,否則儘可能使用記憶體, 這也是Spark效能比Hadoop高的原因。
Spark作業管理
Spark裡面的RDD函式有兩種:
- 轉換函式,呼叫以後得到的還是一個RDD,RDD的計算邏輯主要通過轉換函式完成
- action函式,呼叫以後不再返回RDD。比如count()函式,返回RDD中資料的元素個數;saveAsTextFile(path),將RDD資料儲存到path路徑下。Spark的DAGScheduler在遇到shuffle的時候,會生成一個計算階段,在遇到action函式的時候,會生成一個作業(job)
RDD裡面的每個資料分片,Spark都會建立一個計算任務去處理,所以一個計算階段含多個計算任務(task)。
作業、計算階段、任務的依賴和時間先後關係:
橫軸時間,縱軸任務。兩條粗黑線之間是一個作業,兩條細線之間是一個計算階段。一個作業至少包含一個計算階段。水平方向紅色的線是任務,每個階段由很多個任務組成,這些任務組成一個任務集合。
DAGScheduler根據程式碼生成DAG圖後,Spark任務排程就以任務為單位進行分配,將任務分配到分散式叢集的不同機器上執行。
Spark執行流程
Spark支援Standalone、Yarn、Mesos、K8s等多種部署方案,原理類似,僅是不同元件的角色命名不同。
Spark cluster components:
首先,Spark應用程式啟動在自己的JVM程式裡(Driver程式),啟動後呼叫SparkContext初始化執行配置和輸入資料。SparkContext啟動DAGScheduler構造執行的DAG圖,切分成最小的執行單位-計算任務。
然後,Driver向Cluster Manager請求計算資源,用於DAG的分散式計算。Cluster Manager收到請求後,將Driver的主機地址等資訊通知給叢集的所有計算節點Worker。
Worker收到資訊後,根據Driver的主機地址,跟Driver通訊並註冊,然後根據自己的空閒資源向Driver通報自己可以領用的任務數。Driver根據DAG圖開始向註冊的Worker分配任務。
Worker收到任務後,啟動Executor程式執行任務。Executor先檢查自己是否有Driver的執行程式碼,若無,從Driver下載執行程式碼,通過Java反射載入後開始執行。
總結
相比 Mapreduce,Spark的主要特性:
- RDD程式設計模型更簡單
- DAG切分的多階段計算過程更快
- 使用記憶體儲存中間計算結果更高效
Spark在2012年開始流行,那時記憶體容量提升和成本降低已經比MapReduce出現的十年前強了一個數量級,Spark優先使用記憶體的條件已經成熟。
參考