引入
一般來說,分布式數據集的容錯性有兩種方式:數據檢查點和記錄數據的更新。
面向大規模數據分析,數據檢查點操作成本非常高,須要通過數據中心的網絡連接在機器之間復制龐大的數據集,而網絡帶寬往往比內存帶寬低得多,同一時候還須要消耗很多其它的存儲資源。
因此,Spark選擇記錄更新的方式。可是,假設更新粒度太細太多,那么記錄更新成本也不低。因此。RDD僅僅支持粗粒度轉換,即僅僅記錄單個塊上運行的單個操作,然后將創建RDD的一系列變換序列(每一個RDD都包括了他是怎樣由其它RDD變換過來的以及怎樣重建某一塊數據的信息。因此RDD的容錯機制又稱“血統(Lineage)”容錯)記錄下來,以便恢復丟失的分區。
Lineage本質上非常相似於數據庫中的重做日志(Redo Log),僅僅只是這個重做日志粒度非常大,是對全局數據做相同的重做進而恢復數據。
Lineage機制
Lineage簡單介紹
相比其它系統的細顆粒度的內存數據更新級別的備份或者LOG機制,RDD的Lineage記錄的是粗顆粒度的特定數據Transformation操作(如filter、map、join等)行為。
當這個RDD的部分分區數據丟失時,它能夠通過Lineage獲取足夠的信息來又一次運算和恢復丟失的數據分區。由於這樣的粗顆粒的數據模型,限制了Spark的運用場合,所以Spark並不適用於全部高性能要求的場景,但同一時候相比細顆粒度的數據模型,也帶來了性能的提升。
兩種依賴關系
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的划分點。
依賴關系的特性
第一,窄依賴能夠在某個計算節點上直接通過計算父RDD的某塊數據計算得到子RDD相應的某塊數據;寬依賴則要等到父RDD全部數據都計算完畢之后,並且父RDD的計算結果進行hash並傳到相應節點上之后才干計算子RDD。
第二,數據丟失時,對於窄依賴僅僅須要又一次計算丟失的那一塊數據來恢復。對於寬依賴則要將祖先RDD中的全部數據塊全部又一次計算來恢復。所以在長“血統”鏈特別是有寬依賴的時候,須要在適當的時機設置數據檢查點。
也是這兩個特性要求對於不同依賴關系要採取不同的任務調度機制和容錯恢復機制。
容錯原理
在容錯機制中,假設一個節點死機了。並且運算窄依賴。則僅僅要把丟失的父RDD分區重算就可以,不依賴於其它節點。而寬依賴須要父RDD的全部分區都存在,重算就非常昂貴了。
能夠這樣理解開銷的經濟與否:在窄依賴中。在子RDD的分區丟失、重算父RDD分區時,父RDD相應分區的全部數據都是子RDD分區的數據,並不存在冗余計算。
在寬依賴情況下,丟失一個子RDD分區重算的每一個父RDD的每一個分區的全部數據並非都給丟失的子RDD分區用的,會有一部分數據相當於相應的是未丟失的子RDD分區中須要的數據,這樣就會產生冗余計算開銷,這也是寬依賴開銷更大的原因。因此假設使用Checkpoint算子來做檢查點,不僅要考慮Lineage是否足夠長,也要考慮是否有寬依賴,對寬依賴加Checkpoint是最物有所值的。
Checkpoint機制
通過上述分析能夠看出在下面兩種情況下,RDD須要加檢查點。
- DAG中的Lineage過長,假設重算,則開銷太大(如在PageRank中)。
- 在寬依賴上做Checkpoint獲得的收益更大。
由於RDD是僅僅讀的,所以Spark的RDD計算中一致性不是主要關心的內容,內存相對easy管理,這也是設計者非常有遠見的地方,這樣降低了框架的復雜性,提升了性能和可擴展性,為以后上層框架的豐富奠定了強有力的基礎。
在RDD計算中。通過檢查點機制進行容錯,傳統做檢查點有兩種方式:通過冗余數據和日志記錄更新操作。
在RDD中的doCheckPoint方法相當於通過冗余數據來緩存數據。而之前介紹的血統就是通過相當粗粒度的記錄更新操作來實現容錯的。
檢查點(本質是通過將RDD寫入Disk做檢查點)是為了通過lineage做容錯的輔助。lineage過長會造成容錯成本過高。這樣就不如在中間階段做檢查點容錯,假設之后有節點出現故障而丟失分區。從做檢查點的RDD開始重做Lineage,就會降低開銷。
轉載請注明作者Jason Ding及其出處
GitCafe博客主頁(http://jasonding1354.gitcafe.io/)
Github博客主頁(http://jasonding1354.github.io/)
CSDN博客(http://blog.csdn.net/jasonding1354)
簡書主頁(http://www.jianshu.com/users/2bd9b48f6ea8/latest_articles)
Google搜索jasonding1354進入我的博客主頁