「大數據」(七十九)Spark之Lineage機制

【導讀:數據是二十一世紀的石油,蘊含巨大價值,這是·情報通·大數據技術系列第[79]篇文章,歡迎閱讀和收藏】

1 基本概念

容錯性 :對於流式計算來說,容錯性至關重要。首先我們要明確一下 Spark 中 RDD 的容錯機制。每一個 RDD 都是一個不可變的分佈式可重算的數據集,其記錄著確定性的操作繼承關係( lineage ),所以只要輸入數據是可容錯的,那麼任意一個 RDD 的分區( Partition )出錯或不可用,都是可以利用原始輸入數據通過轉換操作而重新算出的。

一般來說,分佈式數據集的容錯性有兩種方式: 數據檢查點和記錄數據的更新

「大數據」(七十九)Spark之Lineage機制

面向大規模數據分析,數據檢查點操作成本很高,需要通過數據中心的網絡連接在機器之間複製龐大的數據集,而網絡帶寬往往比內存帶寬低得多,同時還需要消耗更多的存儲資源。

因此, Spark 選擇記錄更新的方式。但是,如果更新粒度太細太多,那麼記錄更新成本也不低。因此, RDD 只支持粗粒度轉換,即只記錄單個塊上執行的單個操作,然後將創建 RDD 的一系列變換序列(每個 RDD 都包含了他是如何由其他 RDD 變換過來的以及如何重建某一塊數據的信息。因此 RDD 的容錯機制又稱“血統 (Lineage) ”容錯)記錄下來,以便恢復丟失的分區。

Lineage 本質上很類似於數據庫中的重做日誌( Redo Log ),只不過這個重做日誌粒度很大,是對全局數據做同樣的重做進而恢復數據。


雖然只支持粗粒度轉換限制了編程模型,但我們發現 RDD 仍然可以很好地適用於很多應用,特別是支持數據並行的批量分析應用,包括數據挖掘、機器學習、圖算法等,因為這些程序通常都會在很多記錄上執行相同的操作。 RDD 不太適合那些異步更新共享狀態的應用,例如並行 web 爬行器。

2 術語解釋

RDD : ( Resilient Distributed Datasets 彈性分佈式數據集),是 spark 中最重要的概念,可以簡單的把 RDD 理解成一個提供了許多操作接口的數據集合,和一般數據集不同的是,其實際數據分佈存儲於一批機器中(內存或磁盤中)。它具備像 MapReduce 等數據流模型的容錯特性,並且允許開發人員在大型集群上執行基於內存的計算。

為了有效地實現容錯, RDD 提供了一種高度受限的共享內存,即 RDD 是隻讀的,並且只能通過其他 RDD 上的批量操作來創建。儘管如此, RDD 仍然足以表示很多類型的計算,包括 MapReduce 和專用的迭代編程模型(如 Pregel )等 [2] 。


RDD 是隻讀的、分區記錄的集合。 RDD 只能基於在穩定物理存儲中的數據集和其他已有的 RDD 上執行確定性操作來創建。這些確定性操作稱之為轉換,如 map 、 filter 、 groupBy 、 join (轉換不是程開發人員在 RDD 上執行的操作) [2] 。


RDD 不需要物化。 RDD 含有如何從其他 RDD 衍生(即計算)出本 RDD 的相關信息(即 Lineage ),據此可以從物理存儲的數據計算出相應的 RDD 分區 [2] 。


RDD 作為數據結構,本質上是一個只讀的分區記錄集合。一個 RDD 可以包含多個分區,每個分區就是一個 dataset 片段。 RDD 可以相互依賴。如果 RDD 的每個分區最多隻能被一個 Child RDD 的一個分區使用,則稱之為 narrow dependency ;若多個 Child RDD 分區都可以依賴,則稱之為 wide dependency 。不同的操作依據其特性,可能會產生不同的依賴。例如 map 操作會產生 narrow dependency ,而 join 操作則產生 wide dependency 。


3 Lineage 機制

3.1 Lineage 簡介

相比其他系統的細顆粒度的內存數據更新級別的備份或者 LOG 機制, RDD 的 Lineage 記錄的是粗顆粒度的特定數據 Transformation 操作(如 filter 、 map 、 join 等)行為。當這個 RDD 的部分分區數據丟失時,它可以通過 Lineage 獲取足夠的信息來重新運算和恢復丟失的數據分區。因為這種粗顆粒的數據模型,限制了 Spark 的運用場合,所以 Spark 並不適用於所有高性能要求的場景,但同時相比細顆粒度的數據模型,也帶來了性能的提升。

3.2 兩種依賴關係

RDD 在 Lineage 依賴方面分為兩種:窄依賴 (Narrow Dependencies) 與寬依賴 (Wide Dependencies, 源碼中稱為 Shuffle Dependencies) ,用來解決數據容錯的高效性。

· 窄依賴是指父 RDD 的每一個分區最多被一個子 RDD 的分區所用,表現為一個父 RDD 的分區對應於一個子 RDD 的分區 或多個父 RDD 的分區對應於一個子 RDD 的分區,也就是說一個父 RDD 的一個分區不可能對應一個子 RDD 的多個分區。 1 個父 RDD 分區對應 1 個子 RDD 分區,這其中又分兩種情況: 1 個子 RDD 分區對應 1 個父 RDD 分區(如 map 、 filter 等算子), 1 個子 RDD 分區對應 N 個父 RDD 分區(如 co-paritioned (協同劃分)過的 Join )。

· 寬依賴是指子 RDD 的分區依賴於父 RDD 的多個分區或所有分區,即存在一個父 RDD 的一個分區對應一個子 RDD 的多個分區。 1 個父 RDD 分區對應多個子 RDD 分區,這其中又分兩種情況: 1 個父 RDD 對應所有子 RDD 分區(未經協同劃分的 Join )或者 1 個父 RDD 對應非全部的多個 RDD 分區(如 groupByKey )。

本質理解: 根據父 RDD 分區是對應 1 個還是多個子 RDD 分區來區分窄依賴(父分區對應一個子分區)和寬依賴(父分區對應多個子分區)。如果對應多個,則當容錯重算分區時,因為父分區數據只有一部分是需要重算子分區的,其餘數據重算就造成了冗餘計算。

對於寬依賴, Stage 計算的輸入和輸出在不同的節點上,對於輸入節點完好,而輸出節點死機的情況,通過重新計算恢復數據這種情況下,這種方法容錯是有效的,否則無效,因為無法重試,需要向上追溯其祖先看是否可以重試(這就是 lineage ,血統的意思),窄依賴對於數據的重算開銷要遠小於寬依賴的數據重算開銷。

窄依賴和寬依賴的概念主要用在兩個地方:一個是容錯中相當於 Redo 日誌的功能;另一個是在調度中構建 DAG 作為不同 Stage 的劃分點。

3.3 依賴關係的特性

第一,窄依賴可以在某個計算節點上直接通過計算父 RDD 的某塊數據計算得到子 RDD 對應的某塊數據;寬依賴則要等到父 RDD 所有數據都計算完成之後,並且父 RDD 的計算結果進行 hash 並傳到對應節點上之後才能計算子 RDD 。


第二,數據丟失時,對於窄依賴只需要重新計算丟失的那一塊數據來恢復;對於寬依賴則要將祖先 RDD 中的所有數據塊全部重新計算來恢復。所以在長“血統”鏈特別是有寬依賴的時候,需要在適當的時機設置數據檢查點。也是這兩個特性要求對於不同依賴關係要採取不同的任務調度機制和容錯恢復機制。


3.4 容錯原理

在容錯機制中,如果一個節點死機了,而且運算窄依賴,則只要把丟失的父 RDD 分區重算即可,不依賴於其他節點。而寬依賴需要父 RDD 的所有分區都存在,重算就很昂貴了。可以這樣理解開銷的經濟與否:在窄依賴中,在子 RDD 的分區丟失、重算父 RDD 分區時,父 RDD 相應分區的所有數據都是子 RDD 分區的數據,並不存在冗餘計算。在寬依賴情況下,丟失一個子 RDD 分區重算的每個父 RDD 的每個分區的所有數據並不是都給丟失的子 RDD 分區用的,會有一部分數據相當於對應的是未丟失的子 RDD 分區中需要的數據,這樣就會產生冗餘計算開銷,這也是寬依賴開銷更大的原因。因此如果使用 Checkpoint 算子來做檢查點,不僅要考慮 Lineage 是否足夠長,也要考慮是否有寬依賴,對寬依賴加 Checkpoint 是最物有所值的。

4 Checkpoint 機制

通過上述分析可以看出在以下兩種情況下, RDD 需要加檢查點。

1. DAG 中的 Lineage 過長,如果重算,則開銷太大(如在 PageRank 中)。

2. 在寬依賴上做 Checkpoint 獲得的收益更大。

由於 RDD 是隻讀的,所以 Spark 的 RDD 計算中一致性不是主要關心的內容,內存相對容易管理,這也是設計者很有遠見的地方,這樣減少了框架的複雜性,提升了性能和可擴展性,為以後上層框架的豐富奠定了強有力的基礎。


在 RDD 計算中,通過檢查點機制進行容錯,傳統做檢查點有兩種方式:通過冗餘數據和日誌記錄更新操作。在 RDD 中的 doCheckPoint 方法相當於通過冗餘數據來緩存數據,而之前介紹的血統就是通過相當粗粒度的記錄更新操作來實現容錯的。


分享到:


相關文章: