Spark 經典論文筆記---Resilient Distributed Datasets : A Fault-Tolerant Abstraction for In-Memory Cluster Computing


Spark 經典論文筆記

Resilient Distributed Datasets : A Fault-Tolerant Abstraction for In-Memory Cluster Computing

為什么要設計spark

現在的計算框架如Map/Reduce在大數據分析中被廣泛采用,為什么還要設計新的spark?

  • Map/Reduce提供了高級接口可以方便快捷的調取計算資源,但是缺少對分布式內存有影響的抽象。這就造成了計算過程中需要在機器間使用中間數據,那么只能依靠中間存儲來保存中間結果,然后再讀取中間結果,造成了時延與IO性能的降低。
  • 雖然有些框架針對數據重用提出了相應的解決辦法,比如Pregel針對迭代圖運算設計出將中間結果保存在內存中,HaLoop提供了迭代Map/Reduce的接口,但是這些都是 針對特定的功能設計的不具備通用性。

針對以上問題,Spark提出了一種新的數據抽象模式稱為RDD(彈性分布式數據集),RDD是容錯的並行的數據結構,並且可以讓用戶顯式的將數據保存在內存中,並且可以控制他們的分區來優化數據替代以及提供了一系列高級的操作接口。

RDD數據結構的容錯機制

設計RDD的主要挑戰在與如何設計高效的容錯機制。現有的集群的內存的抽象都在可變狀態(啥是可變狀態)提供了一種細粒度(fine-grained)更新。在這種接口條件下,容錯的唯一方法就是在不同的機器間復制內存,或者使用log日志記錄更新,但是這兩種方法對於數據密集(data-intensive大數據)來說都太昂貴了,因為數據的復制及時傳輸需要大量的帶寬,同時還帶來了存儲的大量開銷。
與上面的系統不同,RDD提供了一種粗粒度(coarse-grained)的變換(比如說map,filter,join),這些變換對數據項應用相同的操作。這樣使用日志記錄數據集是如何產生的而不是去記錄數據本身,使得容錯變得高效。一旦某個parition丟失的話,通過日志可以找出該partition是如何通過其他的RDD經過什么操作得到的,因此可以快速的重新再計算來修正錯誤,同時也不需要數據的復制。

RDD

通常來講,RDD是一種只讀的,分區(partitioned)式的數據集合。RDD創建的方式只用兩種:要不從現有的存儲上讀取數據,要么從別的RDD轉化(transformations)而來。RDD並不不要每時每刻都具體化(理解為計算出結果???),一個RDD包含了它是如何計算出來如果分區的,這種特性帶來一個好處:程序不能引用經過失敗不能重組的RDD(這是啥意思??)。
對於RDD,用戶可以設置的參數為persistencepartitioning。用戶可以決定哪些RDD是需要的重用的然后選擇一種保存策略對該RDD進行保存(肯定有IN-MEMORY,IN-DISK,常駐內存和常駐磁盤)。用戶也可以設定RDD的元素是如何基於每一個Record中的key來進行分區的。

RDD的好處

  1. 一旦某個partition丟失,只需要按照transformations的世襲(lineage)進行重新計算就可以恢復數據,程序不用做任何回滾操作。
  2. A second benefit of RDDs is that their immutable nature lets a system mitigate slow nodes(stragglers) by running backup copies of slow tasks as in MapReduce(沒看懂)

RDD不適宜的場景

數據需要異步更新,或者增量更新的情況下,RDD就不太合適了,因為RDD每次都是都數據做相同的操作,在異步更新中,每個或者每批數據的更新可能都是不相同的。但是在流處理中不就是相當於是增量更新??

RDD的表示

RDD的組成分為以下5個部分:

  • 分區集合,數據的原子片,就是數據的不同存放分區
  • 父RDD的依賴信息
  • 函數,用以從父RDD計算當前的RDD
  • 分區數據存放的數據幀格式的元數據
  • 數據分布RDD接口表如下表所示
Operation Meaning
partitions() 返回一個 Partition 對象的列表
preferredLocations(p) 根據數據列出分區p可以更快的被讀取
dependencies() 返回一系列的依賴
iterator(p,parentIters) 給定父分區的迭代器后計算當前分區的元素的值
partitioner() 返回元數據,指定RDD是否為散列/范圍分區

依賴的種類:
窄依賴:一個父RDD最多被一個子RDD依賴,比如說map操作
寬依賴:一個父RDD可能被多個子RDD所依賴,比如說join操作

為什么要做這樣的依賴區分?
窄依賴可以對某個RDD在某個單獨的節點上順序執行一系列的操作,比如在map之后使用filter等一連串的操作。而寬依賴則需要某節點需要對所有的父節點都可用,可能需要在節點之間對數據進行重新洗牌操作(shuffled)。其二,一旦錯誤發生,窄依賴的錯誤恢復要簡單的多,因為只有一個分區需要被重新計算,其他的不受錯誤影響。相比之下,在具有廣泛依賴性的譜系圖中,單個故障節點可能會導致RDD的所有祖先丟失一些分區,從而需要完全重新執行。

我們在下面描繪一些RDD實現。

  • HDFS.使用DHFS創建RDD時,partition的生成是根據HDFS上的文件塊進行生成的,每一個文件塊都生成一個partition。patitions函數為為每一個文件返回一個分區, 文件塊的偏移量都記錄在Partition對象中。preferredLocations給出了每個文件塊所在的位置,iterator可以讀取該文件塊。
  • map.使用map生成RDD時,其partition信息和其父RDD的相同
  • union.使用union操作后分區也是兩個父RDDpartition的聯合,子分區通過一個窄依賴計算兩個父節點的結果然后合並到一起
  • sample. 和map類似,但是RDD內部保存了一個隨機數生成種子用以隨機挑選父RDD的records。
  • join. join可能時窄依賴也可能時寬依賴也有可能時混合類型的。

Spark的實現

實現Spark總共14000行scala代碼。以下介紹spark的作業調度、內存管理以及檢查點支持(checkpoints)

作業調度

stage划分准則,每個stage中盡量包含更多的窄依賴。每當用戶在RDD上運行actions(例如,計數或保存)時,調度程序將檢查RDD的譜系圖以構建執行階段的DAG。每個階段包含盡可能多的具有窄依賴關系的流水線轉換。階段的邊界是廣泛依賴性所需的shuffle操作,或任何已經計算出的可能使父RDD計算短路的分區。然后,調度程序啟動任務以計算每個階段的丟失分區,直到計算出目標RDD為止。

解釋器的設計

此部分暫時忽略

內存管理

spark針對RDD的保存提供了三種方式:作為序列化的對象保存在內存中,作為序列化的數據保存在內存中,直接保存在硬盤中。

  • 內存中的序列化對象:表現最好,主要在於JVM可以直接讀取每一個RDD元素
  • 內存中的序列化數據:表現次之,但是在內存有限的情況寫很好,
  • 硬盤保存:性能最差,但是對於數據量特別大的情況較為適用
    在內存有限的情況下,我們使用LRU策略進行管理RDD,當得到一個最新的RDD但是空間不足時就從最近一直沒有使用的RDD中彈出一個位置。但是如果新舊相同那么直接保存舊的,新的拋棄避免相同的RDD進行頻繁的入和出。

checkpoint支持

雖然RDD可以通過世襲進行重新計算,但是如果世襲的族譜比較長的話,計算起來就會很耗費時間,所以引入checkpoint的方式,將某個時間點的一些RDD保存在硬盤上,一旦出了問題就直接從檢查點進行恢復,避免重復計算。

Evaluation

與之前的技術進行比較,這里不做過多介紹,有興趣的可以找出該論文進行查看


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM