3.Spark Core 應用解析之RDD概念及創建

1.RDD爲什麼會產生?

        RDD是Spark的基石,是實現Spark數據處理的核心抽象。那麼RDD爲什麼會產生呢?

        Hadoop的MapReduce是一種基於數據集的工作模式,面向數據,這種工作模式一般是從存儲上加載數據集,然後操作數據集,最後寫入物理存儲設備。數據更多面臨的是一次性處理。

        MR的這種方式對數據領域兩種常見的操作不是很高效。第一種是迭代式的算法。比如機器學習中ALS、凸優化梯度下降等。這些都需要基於數據集或者數據集的衍生數據反覆查詢反覆操作。MR這種模式不太合適,即使多MR串行處理,性能和時間也是一個問題。數據的共享依賴於磁盤。另外一種是交互式數據挖掘,MR顯然不擅長。

        MR中的迭代:
在這裏插入圖片描述
        Spark中的迭代:
在這裏插入圖片描述
        我們需要一個效率非常快,且能夠支持迭代計算和有效數據共享的模型,Spark應運而生。RDD是基於工作集的工作模式,更多的是面向工作流。

        但是無論是MR還是RDD都應該具有類似位置感知、容錯和負載均衡等特性。

2 RDD 概述

  • 2.1 什麼是RDD?

        RDD(Resilient Distributed Dataset)叫做分佈式數據集,是Spark中最基本的數據抽象,它代表一個不可變、可分區、裏面的元素可並行計算的集合。在 Spark 中,對數據的所有操作不外乎創建 RDD、轉化已有RDD 以及調用 RDD 操作進行求值。每個 RDD 都被分爲多個分區,這些分區運行在集羣中的不同節點上。RDD 可以包含 Python、Java、Scala 中任意類型的對象, 甚至可以包含用戶自定義的對象。RDD具有數據流模型的特點:自動容錯、位置感知性調度和可伸縮性。RDD允許用戶在執行多個查詢時顯式地將工作集緩存在內存中,後續的查詢能夠重用工作集,這極大地提升了查詢速度

        RDD支持兩種操作:轉化操作和行動操作。RDD 的轉化操作是返回一個新的 RDD的操作,比如 map()和 filter(),而行動操作則是向驅動器程序返回結果或把結果寫入外部系統的操作。比如 count() 和 first()

        Spark採用惰性計算模式,RDD只有第一次在一個行動操作中用到時,纔會真正計算。Spark可以優化整個計算過程。默認情況下,Spark 的 RDD 會在你每次對它們進行行動操作時重新計算。如果想在多個行動操作中重用同一個 RDD,可以使用 RDD.persist() 讓 Spark 把這個 RDD 緩存下來

  • 2.2 RDD 屬性

1).一組分片(Partition),即數據集的基本組成單位。對於RDD來說,每個分片都會被一個計算任務處理,並決定並行計算的粒度。用戶可以在創建RDD時指定RDD的分片個數,如果沒有指定,那麼就會採用默認值。默認值就是程序所分配到的CPU Core的數目。

2).一個計算每個分區的函數。Spark中RDD的計算是以分片爲單位的,每個RDD都會實現compute函數以達到這個目的。compute函數會對迭代器進行復合,不需要保存每次計算的結果。

3).RDD之間的依賴關係。RDD的每次轉換都會生成一個新的RDD,所以RDD之間就會形成類似於流水線一樣的前後依賴關係。在部分分區數據丟失時,Spark可以通過這個依賴關係重新計算丟失的分區數據,而不是對RDD的所有分區進行重新計算。

4).一個Partitioner,即RDD的分片函數。當前Spark中實現了兩種類型的分片函數,一個是基於哈希的HashPartitioner,另外一個是基於範圍的RangePartitioner。只有對於於key-value的RDD,纔會有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函數不但決定了RDD本身的分片數量,也決定了parent RDD Shuffle輸出時的分片數量。

5).一個列表,存儲存取每個Partition的優先位置(preferred location)。對於一個HDFS文件來說,這個列表保存的就是每個Partition所在的塊的位置。按照“移動數據不如移動計算”的理念,Spark在進行任務調度的時候,會盡可能地將計算任務分配到其所要處理數據塊的存儲位置

RDD是一個應用層面的邏輯概念。一個RDD多個分片。RDD就是一個元數據記錄集,記錄了RDD內存所有的關係數據
在這裏插入圖片描述

3. RDD 彈性

1).自動進行內存和磁盤數據存儲的切換
        Spark優先把數據放到內存中,如果內存放不下,就會放到磁盤裏面,程序進行自動的存儲切換

2).基於血統的高效容錯機制
        在RDD進行轉換和動作的時候,會形成RDD的Lineage依賴鏈,當某一個RDD失效的時候,可以通過重新計算上游的RDD來重新生成丟失的RDD數據。

3).Task如果失敗會自動進行特定次數的重試
        RDD的計算任務如果運行失敗,會自動進行任務的重新計算,默認次數是4次。

4).Stage如果失敗會自動進行特定次數的重試
        如果Job的某個Stage階段計算失敗,框架也會自動進行任務的重新計算,默認次數也是4次。

5).Checkpoint和Persist可主動或被動觸發
        RDD可以通過Persist持久化將RDD緩存到內存或者磁盤,當再次用到該RDD時直接讀取就行。也可以將RDD進行檢查點,檢查點會將數據存儲在HDFS中,該RDD的所有父RDD依賴都會被移除。

6).數據調度彈性
        Spark把這個JOB執行模型抽象爲通用的有向無環圖DAG,可以將多Stage的任務串聯或並行執行,調度引擎自動處理Stage的失敗以及Task的失敗。

7).數據分片的高度彈性
        可以根據業務的特徵,動態調整數據分片的個數,提升整體的應用執行效率。

        RDD全稱叫做彈性分佈式數據集(Resilient Distributed Datasets),它是一種分佈式的內存抽象,表示一個只讀的記錄分區的集合,它只能通過其他RDD轉換而創建,爲此,RDD支持豐富的轉換操作(如map, join, filter, groupBy等),通過這種轉換操作,新的RDD則包含了如何從其他RDDs衍生所必需的信息,所以說RDDs之間是有依賴關係的。基於RDDs之間的依賴,RDDs會形成一個有向無環圖DAG,該DAG描述了整個流式計算的流程,實際執行的時候,RDD是通過血緣關係(Lineage)一氣呵成的,即使出現數據分區丟失,也可以通過血緣關係重建分區,總結起來,基於RDD的流式計算任務可描述爲:從穩定的物理存儲(如分佈式文件系統)中加載記錄,記錄被傳入由一組確定性操作構成的DAG,然後寫回穩定存儲。另外RDD還可以將數據集緩存到內存中,使得在多個操作之間可以重用數據集,基於這個特點可以很方便地構建迭代型應用(圖計算、機器學習等)或者交互式數據分析應用。可以說Spark最初也就是實現RDD的一個分佈式系統,後面通過不斷髮展壯大成爲現在較爲完善的大數據生態系統,簡單來講,Spark-RDD的關係類似於Hadoop-MapReduce關係

4.RDD 特點

        RDD表示只讀的分區的數據集,對RDD進行改動,只能通過RDD的轉換操作,由一個RDD得到一個新的RDD,新的RDD包含了從其他RDD衍生所必需的信息。RDDs之間存在依賴,RDD的執行是按照血緣關係延時計算的。如果血緣關係較長,可以通過持久化RDD來切斷血緣關係

  • 4.1.分區

        RDD邏輯上是分區的,每個分區的數據是抽象存在的,計算的時候會通過一個compute函數得到每個分區的數據。如果RDD是通過已有的文件系統構建,則compute函數是讀取指定文件系統中的數據,如果RDD是通過其他RDD轉換而來,則compute函數是執行轉換邏輯將其他RDD的數據進行轉換
在這裏插入圖片描述

  • 4.2.只讀
            如下圖所示,RDD是隻讀的,要想改變RDD中的數據,只能在現有的RDD基礎上創建新的RDD
    在這裏插入圖片描述

        由一個RDD轉換到另一個RDD,可以通過豐富的操作算子實現,不再像MapReduce那樣只能寫map和reduce了,如下圖所示
在這裏插入圖片描述
        RDD的操作算子包括兩類,一類叫做transformations,它是用來將RDD進行轉化,構建RDD的血緣關係;另一類叫做actions,它是用來觸發RDD的計算,得到RDD的相關計算結果或者將RDD保存的文件系統中。下圖是RDD所支持的操作算子列表
在這裏插入圖片描述

  • 4.3 依賴
            RDDs通過操作算子進行轉換,轉換得到的新RDD包含了從其他RDDs衍生所必需的信息,RDDs之間維護着這種血緣關係,也稱之爲依賴。如下圖所示,依賴包括兩種,一種是窄依賴,RDDs之間分區是一一對應的,另一種是寬依賴,下游RDD的每個分區與上游RDD(也稱之爲父RDD)的每個分區都有關,是多對多的關係
    在這裏插入圖片描述
            通過RDDs之間的這種依賴關係,一個任務流可以描述爲DAG(有向無環圖),如下圖所示,在實際執行過程中寬依賴對應於Shuffle(圖中的reduceByKey和join),窄依賴中的所有轉換操作可以通過類似於管道的方式一氣呵成執行(圖中map和union可以一起執行)。
    在這裏插入圖片描述
  • 4.4 緩存
            如果在應用程序中多次使用同一個RDD,可以將該RDD緩存起來,該RDD只有在第一次計算的時候會根據血緣關係得到分區的數據,在後續其他地方用到該RDD的時候,會直接從緩存處取而不用再根據血緣關係計算,這樣就加速後期的重用。如下圖所示,RDD-1經過一系列的轉換後得到RDD-n並保存到hdfs,RDD-1在這一過程中會有個中間結果,如果將其緩存到內存,那麼在隨後的RDD-1轉換到RDD-m這一過程中,就不會計算其之前的RDD-0了
    在這裏插入圖片描述
  • 4.5 checkpoint

        雖然RDD的血緣關係天然地可以實現容錯,當RDD的某個分區數據失敗或丟失,可以通過血緣關係重建。但是對於長時間迭代型應用來說,隨着迭代的進行,RDDs之間的血緣關係會越來越長,一旦在後續迭代過程中出錯,則需要通過非常長的血緣關係去重建,勢必影響性能。爲此,RDD支持checkpoint將數據保存到持久化的存儲中,這樣就可以切斷之前的血緣關係,因爲checkpoint後的RDD不需要知道它的父RDDs了,它可以從checkpoint處拿到數據。

        給定一個RDD我們至少可以知道如下幾點信息:1、分區數以及分區方式;2、由父RDDs衍生而來的相關依賴信息;3、計算每個分區的數據,計算步驟爲:1)如果被緩存,則從緩存中取的分區的數據;2)如果被checkpoint,則從checkpoint處恢復數據;3)根據血緣關係計算分區的數據。

5.RDD 創建

        在Spark中創建RDD的創建方式大概可以分爲三種:(1)從集合中創建RDD;(2)從外部存儲創建RDD;(3)從其他RDD創建。

  • (1).由一個已經存在的Scala集合創建
scala> val rdd1 = sc.parallelize(Array(1, 2, 3, 4, 5))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[17] at parallelize at <console>:24
  • (2).由外部存儲系統的數據集創建,
    外部存儲系統包括本地的文件系統,還有所有Hadoop支持的數據集,比如HDFS、Cassandra、HBase等
scala> val rdd1 = sc.textFile("hdfs://harvey:9000/RELEASE")
rdd1: org.apache.spark.rdd.RDD[String] = hdfs://harvey:9000/RELEASE
MapPartitionsRDD[21] at textFile at <console>:24
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章