Spark學習四:Spark架構設計和RDD的stage劃分

架構設計

學習完Spark學習二:spark基礎理論知識,我們可以對Spark的一些組件和術語應該有了基本的認識,下面介紹Spark的架構設計。本節內容主要參考廈門大學林子雨老師的Spark課程。非常感謝林子雨老師!

如下圖所示。
Spark運行架構包括集羣資源管理器(Cluster Manager)、運行作業任務的工作節點(Worker Node)、每個應用的任務控制節點(driver program)和每個工作節點上負責具體任務的執行進程(Executor)。其中,集羣資源管理器可以是Spark自帶的資源管理器,也可以是YARN或Mesos等資源管理框架。

與Hadoop MapReduce計算框架相比,Spark所採用的Executor有兩個優點:一是利用多線程來執行具體的任務(Hadoop MapReduce採用的是進程模型)(Spark和MapReduce的運行模式在Spark學習二:spark基礎理論知識裏也詳細說明過),減少任務的啓動開銷;二是Executor中有一個BlockManager存儲模塊,會將內存和磁盤共同作爲存儲設備,當需要多輪迭代計算時,可以將中間結果存儲到這個存儲模塊裏,下次需要時,就可以直接讀該存儲模塊裏的數據,而不需要讀寫到HDFS等文件系統裏,因而有效減少了IO開銷;或者在交互式查詢場景下,預先將表緩存到該存儲系統上,從而可以提高讀寫IO性能。
在這裏插入圖片描述


如下圖所示,在Spark中,一個應用(Application)由一個任務控制節點(Driver)和若干個作業(Job)構成,一個作業由多個階段(Stage)構成,一個階段由多個任務(Task)組成。當執行一個應用時,任務控制節點會向集羣管理器(Cluster Manager)申請資源,啓動Executor,並向Executor發送應用程序代碼和文件,然後在Executor上執行任務,運行結束後,執行結果會返回給任務控制節點,或者寫到HDFS或者其他數據庫中。
在這裏插入圖片描述

Spark運行基本流程

如下圖所示,Spark的基本運行流程如下:
(1)當一個Spark應用被提交時,首先需要爲這個應用構建起基本的運行環境,即由任務控制節點(Driver)創建一個SparkContext,由SparkContext負責和資源管理器(Cluster Manager)的通信以及進行資源的申請、任務的分配和監控等。SparkContext會向資源管理器註冊並申請運行Executor的資源;
(2)資源管理器爲Executor分配資源,並啓動Executor進程,Executor運行情況將隨着“心跳”發送到資源管理器上;
(3)SparkContext根據RDD的依賴關係構建DAG圖,DAG圖提交給DAG調度器(DAGScheduler)進行解析,將DAG圖分解成多個“階段”(每個階段都是一個任務集),並且計算出各個階段之間的依賴關係,然後把一個個“任務集”提交給底層的任務調度器(TaskScheduler)進行處理;Executor向SparkContext申請任務,任務調度器將任務分發給Executor運行,同時,SparkContext將應用程序代碼發放給Executor;
(4)任務在Executor上運行,把執行結果反饋給任務調度器,然後反饋給DAG調度器,運行完畢後寫入數據並釋放所有資源。
在這裏插入圖片描述
總體而言,Spark運行架構具有以下特點:
(1)每個應用都有自己專屬的Executor進程,並且該進程在應用運行期間一直駐留。Executor進程以多線程的方式運行任務,減少了多進程任務頻繁的啓動開銷,使得任務執行變得非常高效和可靠;
(2)Spark運行過程與資源管理器無關,只要能夠獲取Executor進程並保持通信即可;
(3)Executor上有一個BlockManager存儲模塊,類似於鍵值存儲系統(把內存和磁盤共同作爲存儲設備),在處理迭代計算任務時,不需要把中間結果寫入到HDFS等文件系統,而是直接放在這個存儲系統上,後續有需要時就可以直接讀取;在交互式查詢場景下,也可以把表提前緩存到這個存儲系統上,提高讀寫IO性能;
(4)任務採用了數據本地性和推測執行等優化機制。數據本地性是儘量將計算移到數據所在的節點上進行,即“計算向數據靠攏”,因爲移動計算比移動數據所佔的網絡資源要少得多。而且,Spark採用了延時調度機制,可以在更大的程度上實現執行過程優化。比如,擁有數據的節點當前正被其他的任務佔用,那麼,在這種情況下是否需要將數據移動到其他的空閒節點呢?答案是不一定。因爲,如果經過預測發現當前節點結束當前任務的時間要比移動數據的時間還要少,那麼,調度就會等待,直到當前節點可用。


RDD之間的依賴關係

RDD中不同的操作會使得不同RDD中的分區會產生不同的依賴。RDD中的依賴關係分爲窄依賴(Narrow Dependency)與寬依賴(Wide Dependency),下圖展示了兩種依賴之間的區別。
在這裏插入圖片描述

  • 窄依賴,表現爲一個父RDD的分區對應於一個子RDD的分區,或多個父RDD的分區對應於一個子RDD的分區;比如上圖中,RDD1是RDD2的父RDD,RDD2是子RDD,RDD1的分區1,對應於RDD2的一個分區(即分區4);再比如,RDD6和RDD7都是RDD8的父RDD,RDD6中的分區(分區15)和RDD7中的分區(分區18),兩者都對應於RDD8中的一個分區(分區21)。簡記爲窄依賴一對一,父RDD的一個分區對應子RDD的一個分區
  • 寬依賴,則表現爲存在一個父RDD的一個分區對應一個子RDD的多個分區。比如上圖中,RDD9是RDD12的父RDD,RDD9中的分區24對應了RDD12中的兩個分區(即分區27和分區28)。間記爲寬依賴一對多,父RDD的一個分區對應子RDD的多個分區

總體而言,如果父RDD的一個分區只被一個子RDD的一個分區所使用就是窄依賴,否則就是寬依賴。窄依賴典型的操作包括map、filter、union等,寬依賴典型的操作包括groupByKey、sortByKey等。對於連接(join)操作,可以分爲兩種情況。

  • (1)對輸入進行協同劃分,屬於窄依賴(如圖所示)。所謂協同劃分(co-partitioned)是指多個父RDD的某一分區的所有“鍵(key)”,落在子RDD的同一個分區內,不會產生同一個父RDD的某一分區,落在子RDD的兩個分區的情況。
  • (2)對輸入做非協同劃分,屬於寬依賴,如圖所示。

對於窄依賴的RDD,可以以流水線的方式計算所有父分區,不會造成網絡之間的數據混合。對於寬依賴的RDD,則通常伴隨着Shuffle操作,即首先需要計算好所有父分區數據,然後在節點之間進行Shuffle。

stage的劃分

Spark通過分析各個RDD的依賴關係生成了DAG,再通過分析各個RDD中的分區之間的依賴關係來決定如何劃分階段,具體劃分方法是:在DAG中進行反向解析,遇到寬依賴就斷開,遇到窄依賴就把當前的RDD加入到當前的階段中;將窄依賴儘量劃分在同一個階段中,可以實現流水線計算(具體的階段劃分算法請參見AMP實驗室發表的論文《Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing》)。

例如,如下圖所示,假設從HDFS中讀入數據生成3個不同的RDD(即A、C和E),通過一系列轉換操作後再將計算結果保存回HDFS。對DAG進行解析時,在依賴圖中進行反向解析,由於從RDD A到RDD B的轉換以及從RDD B和F到RDD G的轉換,都屬於寬依賴,因此,在寬依賴處斷開後可以得到三個階段,即階段1、階段2和階段3。可以看出,在階段2中,從map到union都是窄依賴,這兩步操作可以形成一個流水線操作,比如,分區7通過map操作生成的分區9,可以不用等待分區8到分區9這個轉換操作的計算結束,而是繼續進行union操作,轉換得到分區13,這樣流水線執行大大提高了計算的效率。
在這裏插入圖片描述

RDD運行過程

(1)創建RDD對象;
(2)SparkContext負責計算RDD之間的依賴關係,構建DAG;
(3)DAGScheduler負責把DAG圖分解成多個階段,每個階段中包含了多個任務,每個任務會被任務調度器分發給各個工作節點(Worker Node)上的Executor去執行。
在這裏插入圖片描述

參考網址

廈門大學林子雨老師的Spark課程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章