Spark RDD

轉載自
1、RDD 全稱 彈性分佈式數據集 Resilient Distributed Dataset
它就是一個 class。

abstract class RDD[T: ClassTag](
    @transient private var _sc: SparkContext,
    @transient private var deps: Seq[Dependency[_]]
  ) extends Serializable with Logging {

繼承了 Serializable 和具有 Logging 的特質,爲什麼要Serializable?答:因爲不同的 RDD 之間需要進行轉化(序列化:數據轉化成二進制,反序列:化二進制轉化爲數據)。

2、RDD 其實是 spark 爲了減少用戶對於不同數據結構之間的差異而提供的數據封裝,爲用戶提供了很多數據處理的操作。

3、RDD 三個特點
  3.1、不可分,在 RDD 上調用轉換算子,會生成一個新的 RDD,不會更改原 RDD 的數據結構。
  3.2、可分區,RDD 的數據可以根據配置分成多個分區,每個分區都被一個 Task 任務去處理,可以認爲分區數就是並行度。
  3.3、彈性:
    3.3.1、存儲的彈性,RDD 的數據可以在內存和磁盤進行自動切換,對用戶透明。
    3.3.2、計算的彈性,RDD 的計算之間會有重試機制,避免由於網絡等原因導致的任務失敗。
    3.3.3、容錯的彈性,RDD 可以通過血統機制來進行 RDD 的恢復。
    3.3.4、分區的彈性,可以根據需求來動態改變 RDD 分區的分區數,也就是動態改變了並行度。

4、Spark 到底做了什麼?

簡言之:從外部空間將數據加載到 Spark,對數據進行轉換、緩存最後將數據通過行動操作保存到外部空間。
5、RDD 兩種處理數據的方式
RDD 有兩種處理數據的方式,一種叫轉換操作【一個 RDD 調用該方法後返回一個 RDD】,另外一種叫行動操作【一個 RDD 調用該方法後返回一個標量或者直接將數據保存到外部空間】。

6、RDD 是懶執行的,如果沒有行動操作出現,所有的轉換操作都不會執行。
具體有哪些算子操作參見Spark Operator

7、向 RDD 操作傳遞函數注意
  傳遞函數的時候需要注意:如果你的 RDD 轉換操作中的函數使用到了類的方法或者變量,那麼你需要注意該類可能需要能夠序列化。即該 class 需要繼承 java.io.Serializable 接口,或者可以將屬性賦值爲本地變量來防止整個對象的傳輸。

8、RDD的依賴關係
  窄依賴(narrow dependency):子的父依賴只有一個,出度1。
  寬依賴(wide dependency):子的父依賴有多個,出度大於2。
  RDD 之間的前後依賴關係有寬依賴和窄依賴之分,主要通過依賴的不同來劃分 Stage(階段)。
  區別:是否要進行 shuffle 階段(即合併分區的過程)。

9、RDD 的任務切分
  Application:一個能夠打成 jar 包的 Spark 程序就是一個應用。裏面應該有一個 SparkContext。
  Job:一個應用中每一個 Action 操作所涉及到的所有轉換叫一個 Job。
  Stage:一個 Job 根據 RDD 之間的寬窄依賴關係劃分爲多個 Stage,Stage 之間是根據依賴關係來逐個執行的。
  Task: 一個 Stage 運行的時候,RDD 的每一個分區都會被一個 Task 去處理,也可以認爲是並行度。
10、RDD 的運行規劃
  寫代碼我們都是從前往後寫,劃分 Stage 是從後往前劃分,步驟如下:
  (1)首先先把所有代碼劃分成爲一個 Stage,然後該 Stage 入棧。
  (2)從最後的代碼往前走,如果發現 RDD 之間的依賴關係是寬依賴,那麼將寬依賴前面的所有代碼劃分爲第二個 Stage,然後該 Stage 入棧。
  (3)根據2規則繼續往前走,直到代碼開頭。
11、RDD 持久化
  RDD 持久化:每一個節點都將把計算的分片結果保存在內存中,並在對此 RDD 或衍生出的 RDD 進行的其他動作中重用。(防止重新計算浪費資源,因爲 RDD 在沒有持久化的時候默認計算的分片結果是不保存的,如果需要那麼就要根據血統關係來重新計算。)
  持久化也是懶執行的,持久化有兩個操作:persist(StorageLevel),persist() 默認把數據以序列化的形式緩存在 jvm 的堆空間中;另外一個是 cache,cache 就相當於 MEMORY_ONLY 的 persist。
  使用步驟:

// 設置緩存級別:MEMORY_ONLY, MEMORY_ONLY_SER
data.persist(StorageLevel.DISK_ONLY)
// 清除緩存
data.unpersist
// data.unpersist(blocking=true)
持久化級別按照:存儲的位置(磁盤、內存、非堆內存)、是否序列化、存儲的份數(1,2)進行劃分

12、RDD 檢查點機制
  檢查點也是一種 RDD 的持久化機制,只不過檢查點將 RDD 的數據放在非易失存儲上,比如 HDFS,存放之後會將 RDD 的依賴關係刪除,主要是因爲檢查點機制認爲該 RDD 不會丟失。
如何用呢?步驟如下:
  (1)通過 sc.setCheckPointDir(“hdfs://hadoop102:9000/checkpoint”) 來設置一個 HDFS 兼容的文件系統目錄
  (2)通過在RDD.checkPoint() 來啓用檢查點
  (3)RDD 創建之初就要啓用檢查點,否則不成功
注意:整個 checkpoint 的讀取是用戶透明的(即用戶看不到,是後臺執行的)。
13、鍵值對 RDD 的數據分區
  hash 分區:對於給定的 key,計算其 hashCode,併除於分區的個數取餘,容易造成數據傾斜。
  range 分區:採用的是水塘抽樣算法,將將一定範圍內的數映射到某一個分區內,避免了一個數據傾斜的狀態。
  主要有 Hash 和 Range 兩種,Range 分區通過水塘抽樣算法來保證每一個分區的數據都比較均勻。
  可以通過繼承 Partitoner 來實現自定義的分區器,複寫2個方法。
  scala 獲取分區數的元素:res3.mapPartitionsWithIndex((index, iter) => Iterator(index + “—” + iter.mkString(" , "))).collect
14、RDD 累加器
  RDD 累加器:線程安全,不是針對某個節點或者某個 RDD 的,它的對象是整個 Spark,類似於 hadoop 的累加器。
  RDD 累加器是提供一個類似於共享變量的東西,能夠在 Driver 的數據空間定義,然後在 Executor 的數據空間進行更新,然後在 Driver 的數據空間進行正確訪問的機制。
  14.1、使用系統默認提供的累加器(沒啥用),步驟如下:
  (1)申請一個 val blanklines = sc.accumulator(0)
  (2)在轉換操作中使用累加器要注意,可以回出現重複累加的情況,所以最好是在行動操作中使用。使用的時候不能夠訪問只能夠更新,方法是:blanklines.add()。
  (3)在 Driver 程序中去訪問,方法是: blanklines.value。
  14.2、自定義累加器,主要操作如下:
  (1)需要繼承 AccumulatorV2 這個虛擬類,然後提供類型參數:1) 增加值的類型參數,2) 輸出值的類型參數。然後重寫5個方法。
  (2)需要創建一個 SparkContext
  (3)需要創建一個自定義累加器實例
  (4)需要通過 SparkContext 去註冊你的累加器, sc.register(accum, “logAccum”)
  (5)需要在轉換或者行動操作中使用累加器。
  (6)在Driver中輸出累加器的結果。

		val conf = new SparkConf().setMaster("local[*]")setAppName("LogAccumulator")
		val sc = new SparkContext(conf)
		val accum = new LogAccumulator
		sc.register(accum, "logAccum")
		
		accum.add(x)

15、廣播變量
  (1)如果轉換操作中使用到了 Driver 程序中定義的變量,如果該變量不是通過廣播變量來進行聲明的,那麼每一個分區都會拷貝該變量一份,會造成大量的網絡數據傳輸。(廣播傳輸,帶寬浪費嚴重!)
  (2)如果使用廣播變量來聲明該共享變量,那麼只會在每一個 Executor 中存在一次拷貝。(因爲每一個 Executor 中有成千上萬個分區!)
  (3)廣播變量主要用來分發只讀的小數據集,比如 300M 左右。
  (4)怎麼使用廣播變量呢?步驟如下:
    1. 先聲明 val broadcastVar = sc.broadcast(Array(1, 2, 3))
    2. 再訪問 broadcastVar.value

16、Spark Core 數據讀取與存儲的主要方式
  (1)文本文件的輸入輸出:textFile 和 saveAsTextFile,注意:在寫出 text 文件的時候,每一個 partition 會單獨寫出,文件系統支持所有和 Hadoop 文件系統兼容的文件系統。
  (2)JSON 文件或者 CSV 文件:
    這種有格式的文件的輸入和輸出還是通過文本文件的輸入和輸出來支持的,Spark Core 沒有內置對 JSON 文件和 CSV 文件的解析和反解析功能,這個解析功能是需要用戶自己根據需求來定製的。
注意:JSON 文件的讀取如果需要多個 partition 來讀,那麼 JSON 文件一般一行是一個 json。如果你的 JSON 是跨行的,那麼需要整體讀入所有數據,並整體解析。
  (3)Sequence 文件:Spark 有專門用來讀取 SequenceFile 文件的接口。可以直接使用
  sequenceFile[keyClass, valueClass](path) 進行讀取。注意:針對於 HDFS 中的文件 block 數爲 1,那麼 Spark 設定了最小的讀取 partition 數爲 2。如果 HDFS 中的文件 block 數爲大於 1,比如 block 數爲 5,那麼 Spark 的讀取 partition 數爲 5。(因爲 Spark 本質上屬於內存計算層,它的輸入輸出很大一部分依賴於 HDFS 文件系統。)
  (4)ObjectFile 文件:sc.saveAsObjectFile 輸出的是對象的形式
    1. 對於 ObjectFile 它的讀取和保存使用了讀取和保存 SequenceFile 的 API,也最終調用了 hadoop 的 API。
    2. ObjectFile 的讀取使用 objectFile 進行。
    3. ObjectFile 的輸出直接使用 saveAsObjectFile 來進行輸出。
    4. 需要注意的是:在讀取 ObjectFile 的時候需要指定對象的類型,而並不是 K-V 的類型。
  (5)HadoopAPI 的讀取和輸入:
    讀取:newApiHadoopFile 和 newApiHadoopRDD 兩個方法,最終都是調用 newApiHadoopRDD 來進行實現。
    輸出:saveAsNewApiHadoopFile 和 saveAsNewApiHadoopDataset 兩個方法,最終都是調用 saveAsNewApiHadoopDataset 這個方法進行實現。
  (6)關係型數據庫的輸入輸出:JdbcRDD 裏面包括了驅動,數據庫用戶名/密碼
    1. 對於關係型數據庫的讀取使用 JdbcRDD 來進行實現,JdbcRDD 需要依次傳入 sparkContext、獲取 JDBC Connection 的無參方法、查詢數據庫的 SQL 語句,id 的下界、id 的上界、分區數、提供解析 ResultSet 的函數。
    2. 對於關係型數據庫的輸出,直接採用 jdbc 執行 insert 語句或者 update 語句進行實現。

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