大數據,Spark之RDD,RDD詳細講解(二)

一、RDD的特性

Spark之所以成爲目前比較主流的大數據處理技術,其中RDD的特性和機制佔到很大比重,沒有RDD的這些機制,Spark性能會大打折扣。總體而言,Spark採用RDD後能夠實現高效計算的主要原因有以下幾點:

1、高效的容錯機制。

現有的分佈式共享內存、鍵值存儲、內存數據庫等,爲了實現容錯,必須在集羣節點之間進行數據複製(主從複製)或者記錄日誌。

這就造成各個節點之間大量數據傳輸,這對於數據密集型應用而言會帶來很大的開銷。而在設計RDD的過程中,賦予RDD數據只讀的能力,不可被修改。如果需要修改數據,必須從父RDD轉換到子RDD從新進行計算,由此在不同RDD之間建立了血緣關係。

RDD是一種天生具有容錯機制的特殊集合,不需要通過數據冗餘的方式實現容錯,而只需通過RDD父子依賴(血緣)關係重新計算得到丟失的分區來實現容錯。而不需要回滾整個系統,這樣就避免了數據複製的高開銷。而且重算過程可以在不同節點之間並行進行,實現了高效的容錯。

 

最主要的,RDD提供的轉換操作都是一些粗粒度的操作。比如map、filter和join這些轉換算子,並不觸發真正的計算,只記錄他們之間的邏輯關係,只有當遇到count、foreach這類輸出算子才觸發計算機制。

RDD依賴關係只需要記錄這種粗粒度的轉換操作,而不需要記錄具體的數據和各種細粒度操作的日誌,比如對哪個數據項進行了修改,這就大大降低了數據密集型應用中的容錯開銷。

2、中間結果持久化落地到內存。

數據在內存中的多個RDD操作之間進行傳遞,不需要“落地”到磁盤上,避免了不必要的讀寫磁盤的網絡和IO開銷,有效地提升了數據之間的處理或轉換效率。

3、存放的數據可以是Java對象。

存放的數據可以是Java對象,避免了不必要的對象序列化和反序列化開銷。

 

二、RDD的窄依賴和寬依賴

不同的操作會使得不同RDD中的分區會產生不同的依賴。RDD中的依賴關係分爲窄依賴與寬依賴。

1、什麼是窄依賴和寬依賴?

窄依賴:一個父RDD的分區對應於一個子RDD的分區,或多個父RDD的分區對應於一個子RDD的分區。最終結果是一個或多個父RDD分區對於一個子RDD分區,是 一或多 對一 的關係。窄依賴典型的操作包括map、filter、union等算子。

 

寬依賴:一個父RDD的一個分區對應一個子RDD的多個分區。是一個分區對應多個分區的關係,一對多。寬依賴典型的操作包括groupByKey、sortByKey等算子。

 

總的來說,如果父RDD的一個分區只被一個子RDD的一個分區所使用就是窄依賴,否則就是寬依賴。

特別注意:對於連接Join操作的窄寬依賴的劃分可以分爲兩種情況。

(1)、對輸入進行協同劃分,屬於窄依賴。所謂協同劃分是指多個父RDD的某一分區的所有“鍵”,落在子RDD的同一個分區內,不會產生同一個父RDD的某一分區,落在子RDD的兩個分區的情況。

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

 

2、窄依賴與寬依賴的比較

RDD的這種窄依賴和寬依賴的關係設計,使其具有了天生的容錯性,大大加快了Spark的執行速度。

RDD數據集通過“血緣關係”記錄了它是如何從其它RDD中轉換過來的,血緣關係記錄的是粗數據粒度的轉換操作行爲。當這個RDD的部分分區數據丟失時,可以通過血緣關係獲取足夠的信息來重新運算和恢復丟失的數據分區,由此帶來了性能的提升。

在兩種依賴關係中,窄依賴的失敗恢復更爲高效,它只需要根據父RDD分區重新計算丟失的分區即可,而且可以並行地在不同節點進行重新計算。對於寬依賴而言,單個節點失效通常意味着重新計算過程會涉及多個父RDD分區,開銷較大。

Spark還提供了數據檢查點和記錄日誌,用於持久化中間RDD,從而使得在進行失敗恢復時不需要追溯到最開始的階段。在進行故障恢復時,Spark會對數據檢查點開銷和重新計算RDD分區的開銷進行比較,從而自動選擇最優的恢復策略。

 

三、RDD的階段劃分

通過分析各個RDD的依賴關係生成了DAG,再通過分析各個RDD中的分區之間的依賴關係來決定如何劃分階段。

劃分方法:對DAG進行反向解析,遇到寬依賴就斷開,遇到窄依賴就把當前的RDD加入到當前的階段中;將窄依賴儘量劃分在同一個階段中,可以實現流水線計算。

 

如上圖,從HDFS中讀入數據生成3個不同的RDD,即A、C和E。通過一系列轉換操作後再將計算結果保存回HDFS。

對DAG進行解析時,在依賴圖中進行反向解析。從RDD A到RDD B的轉換,以及從RDD B和RDD F到RDD G的轉換,都屬於寬依賴。

因此,在寬依賴處斷開後可以得到三個階段,即階段1、階段2和階段3。在階段2中,從map到union都是窄依賴,這兩步操作可以形成一個流水線操作。如,分區7通過map操作生成的分區9,可以不用等待分區8到分區10這個轉換操作的計算結束,而是繼續進行union操作,轉換得到分區13,這樣流水線執行大大提高了計算的效率。

把一個DAG圖劃分成多個“階段”以後,每個階段都代表了一組關聯的、相互之間沒有Shuffle依賴關係的任務組成的任務集合。每個任務集合會被提交給任務調度器TaskScheduler進行處理,由任務調度器將任務分發給Executor運行。

 

四、Spark的RDD運行過程

 

通過對RDD概念、依賴關係和階段劃分的介紹,結合之前介紹的Spark運行基本流程,這裏再總結一下RDD在Spark架構中的運行過程:

1、創建RDD對象

2、SparkContext負責計算RDD之間的依賴關係,構建DAG;

3、DAGScheduler負責把DAG圖分解成多個階段,每個階段中包含了多個任務,每個任務會被任務調度器分發給各個工作節點(Worker Node)上的Executor去執行。

 

一個完整的應用從提交到執行的完整流程:

1,通過spark-submit命令,提交spark應用程序。

2,在driver端會執行代碼,首先通過構造的sparkConf,把配置傳給sparkContext對象,用於創建sparkContext上下文環境。

3,在遇到action算子時,會生成一個Job。DAGScheduler會將Job劃分爲多個Stage,每個Stage會創建一個任務集taskSet。

4,把任務集傳遞給TaskScheduler,TaskScheduler會執行以下過程:

4.1,TaskScheduler會去連接Master向其申請註冊Application。Master接收到App的註冊請求時,會爲其在Worker節點上啓動多個Executor。

4.2,Executor啓動以後會反向註冊到TaskScheduler。Executor會啓動線程池,TaskRunner就是把函數反序列化之後通過線程去運行的。Task有兩個ShuffleMapTask和ResultTask,只有最後一個Stage是ResultTask,之前所有的都是ShuffleMapTask。

4.3,TaskScheduler接到Executor註冊請求後,會把任務集裏的每一個任務都提交到Executor上執行。在這裏有任務分配的算法,移動計算不移動數據。

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