Spark原理篇之RDD特徵分析講解

      RDD(Resilient Distributed DataSets)彈性分佈式數據集,是分佈式內存的一個抽象概念。我們可以抽象地代表對應一個HDFS上的文件,但是它實際上是被分區的,分爲多個分區灑落在Spark集羣中的不同節點上。

1 RDD五大特性

在這裏插入圖片描述
(1)A list of partitions:RDD是由一組partition組成。例如要讀取hdfs上的文本文件的話,可以使用textFile()方法把hdfs的文件加載過來,把每臺機器的數據放到partition中,並且封裝了一個HadoopRDD,這就是一個抽象的概念。每一個partition都對應了機器中的數據。因爲在hdfs中的一個Datanode,有很多的block,讀機器的數據時,會將每一個block變成一個partition,與MapReduce中split的大小由min split,max split,block size (max(min split, min(max split, block size)))決定的相同,spark中的partition大小實際上對應了一個split的大小。經過轉化,HadoopRDD會轉成其他RDD,如FilteredRDD、PairRDD等,但是partition還是相應的partition,只是因爲有函數應用裏面的數據變化了。
(2)A function for computing each split:在HadoopRDD中,每個partition相當於一個split。一個函數應用在一個RDD上,可以理解爲一個函數對集合(RDD)內的每個元素(partition)的操作。從上圖可以看出HadoopRDD中的partition需要函數的作用才能轉換到FilterRDD中的partition。
(3)A list of dependencies on other RDDs:RDD是有依賴的。PairRDD的依賴是HadoopRDD和FilterRDD,FilterRDD的依賴是HadoopRDD。
(4)Optionally, a Partitioner for key-value RDDs:如果數據是key-value鍵值對形式們可以傳遞一個partitioner來對RDD進行自定義分區。
(5)Optionally,a list of preferred locations to compute each split on: textFile()過程中,可以指定加載到性能好的機器中。例如,HDFS中的數據可能放在一大堆破舊的機器上,HDFS數據在磁盤上,磁盤可能很大,CPU、內存的性能很差。Spark默認做的事情是,把數據加載進來,會把數據抽象成一個RDD,抽象進來的數據在內存中,這內存指的是本機的內存,這是因爲在分佈式文件系統中,要遵循數據本地性原則,即移動計算(把函數、jar包發過去)而不移動數據(移動數據成本較高)。而一般HDFS的集羣機器的內存比較差,如果要把這麼多數據加載到爛機器的內存中,會存在問題,一是內存可能裝不下,二是CPU差、計算能力差,這就等於沒有發揮出spark的性能。在這種情況下,Spark的RDD可以提供一個可選項,可以指定一個preferred locations,即指定一個位置來加載數據。這樣就可以指定加載到性能好的機器去計算。例如,可以將hdfs數據加載到Tachyon內存文件系統中,然後再基於Tachyon來做Spark程序。

2 RDD兩種操作

2.1 Transformation

      Transformation用於對RDD的創建,RDD只能使用Transformation創建,同時還提供大量操作方法,包括map、filter、groupBy和join等,RDD利用這些操作生成新的RDD,但是需要注意,無論多少次Transformation,在RDD中真正數據計算Action之前都不可能真正運行。

2.2 Action

      Action是數據執行部分,其通過執行count、reduce和collect等方法真正執行數據的計算部分。實際上,RDD中所有的操作都是lazy模式進行,運行在編譯中不會立即計算最終結果,而是胡這個月所有操作步驟和方法,只有遇到啓動命令才執行。這樣做的好處在於大部分前期工作在Transformation時已經完成,當Action工作時,只需要完成業務的核心工作。

2.3 函數概覽

2.3.1 Transformation操作函數

(1)map(func):對調用map的RDD數據集中的每個element都使用func,然後返回一個新的RDD,這個返回的數據集是分佈式的數據集。
(2) filter(func) : 對調用filter的RDD數據集中的每個元素都使用func,然後返回一個包含使func爲true的元素構成的RDD。
(3)flatMap(func):和map差不多,但是flatMap生成的是多個結果。
(4)mapPartitions(func):和map很像,但是map是每個element,而mapPartitions是每個partition。
(5)mapPartitionsWithSplit(func):和mapPartitions很像,但是func作用的是其中一個split上,所以func中應該有index。
(6)sample(withReplacement,faction,seed):抽樣。
(7)union(otherDataset):返回一個新的dataset,包含源dataset和給定dataset的元素的集合。
(8)distinct([numTasks]):返回一個新的dataset,這個dataset含有的是源dataset中的distinct的element。
(9)groupByKey(numTasks):返回(K,Seq[V]),也就是hadoop中reduce函數接受的key-valuelist。
(10)reduceByKey(func,[numTasks]):就是用一個給定的reduce func再作用在groupByKey產生的(K,Seq[V]),比如求和,求平均數。
(11)sortByKey([ascending],[numTasks]):按照key來進行排序,是升序還是降序,ascending是boolean類型。
(12)join(otherDataset,[numTasks]):當有兩個KV的dataset(K,V)和(K,W),返回的是(K,(V,W))的dataset,numTasks爲併發的任務數。
(13)cogroup(otherDataset,[numTasks]):當有兩個KV的dataset(K,V)和(K,W),返回的是(K,Seq[V],Seq[W])的dataset,numTasks爲併發的任務數。
(14)cartesian(otherDataset):笛卡爾積就是m*n,大家懂的。

2.3.2 Action操作函數

(1)reduce(func):說白了就是聚集,但是傳入的函數是兩個參數輸入返回一個值,這個函數必須是滿足交換律和結合律的。
(2)collect():一般在filter或者足夠小的結果的時候,再用collect封裝返回一個數組。
(3)count():返回的是dataset中的element的個數。
(4)first():返回的是dataset中的第一個元素。
(5)take(n):返回前n個elements,這個士driver program返回的。
(6)takeSample(withReplacement,num,seed):抽樣返回一個dataset中的num個元素,隨機種子seed。
(7)saveAsTextFile(path):把dataset寫到一個text file中,或者hdfs,或者hdfs支持的文件系統中,spark把每條記錄都轉換爲一行記錄,然後寫到file中。
(8)saveAsSequenceFile(path):只能用在key-value對上,然後生成SequenceFile寫到本地或者hadoop文件系統。
(9)countByKey():返回的是key對應的個數的一個map,作用於一個RDD。
(10)foreach(func):對dataset中的每個元素都使用func。

3 RDD緩存策略

(1)NONE

  val NONE = new StorageLevel(false, false, false, false)

      表示不需要緩存。(不使用磁盤,不用內存,使用堆,序列化)
(2)DISK_ONLY

  val DISK_ONLY = new StorageLevel(true, false, false, false)

      表示使用磁盤。(使用磁盤,不用內存,使用堆,序列化)
(3)DISK_ONLY_2

  val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2)

      DISK_ONLY_2表示使用磁盤,兩個副本。(使用磁盤,不用內存,使用堆,序列化,2)
(4)MEMORY_ONLY

  val MEMORY_ONLY = new StorageLevel(false, true, false, true)

      MEMORY_ONLY表示只使用內存,例如1G的數據要放入512M的內存,會將數據切成兩份,先將512M加載到內存,剩下的512M還在原來位置(如HDFS),之後如果有RDD的運算,會從內存和磁盤中去找各自的512M數據。(不使用磁盤,使用內存,使用堆,不序列化)
(5)MEMORY_ONLY_2

  val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2)

      MEMORY_ONLY_2表示只使用內存,2個副本。(不使用磁盤,使用內存,使用堆,不序列化,2)
(6)MEMORY_ONLY_SER

  val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false)

      MEMORY_ONLY_SER表示只使用內存,序列化。(不使用磁盤,使用內存,使用堆,序列化)
(7)MEMORY_ONLY_SER_2

  val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2)

      MEMORY_ONLY_SER表示只使用內存,序列化,2個副本。(不使用磁盤,使用內存,使用堆,序列化,2)
(8)MEMORY_AND_DISK

  val MEMORY_AND_DISK = new StorageLevel(true, true, false, true)

      MEMORY_AND_DISK和MEMORY_ONLY很類似,都使用到了內存和磁盤,只是使用的是本機本地磁盤,例如1G數據要加載到512M的內存中,首先將HDFS的1G數據的512M加載到內存,另外的512M加載到本地的磁盤緩存着(和HDFS就沒有關係了),RDD要讀取數據的話就在內存和本地磁盤中找。(使用磁盤,使用內存,使用堆,不序列化)
(9)MEMORY_AND_DISK_2

  val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2)

      MEMORY_AND_DISK_2表示兩個副本。(使用磁盤,使用內存,使用堆,不序列化,2)
(10)MEMORY_AND_DISK_SER

  val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false)

      MEMORY_AND_DISK_SER本地內存和磁盤,序列化。序列化的好處在於可以壓縮,但是壓縮就意味着要解壓縮,需要消耗一些CPU。
(11)MEMORY_AND_DISK_SER_2

  val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2)

      MEMORY_AND_DISK_SER2,兩個副本。
(12)OFF_HEAP

  val OFF_HEAP = new StorageLevel(false, false, true, false)

      OFF_HEAP不使用堆內存(例如可以使用Tachyon的分佈式內存文件系統)。(不使用磁盤,不用內存,不使用堆,序列化)
4 RDD Lineage(血統)
在這裏插入圖片描述
      一系列RDD到RDD的transformation操作,稱爲lineage(血統)。某個RDD依賴於它前面的所有RDD。例如一個由10個RDD到RDD的轉化構成的lineage,如果在計算到第9個RDD時失敗了,一般較好的計算框架會自動重新計算。一般地,這種錯誤發生了會去找上一個RDD,但是實際上如果不做緩存是找不到的,因爲即使RDD9知道它是由RDD8轉化過來的,但是因爲它並沒有存RDD數據本身,在內存中RDD瞬時轉化,瞬間就會在內存中消失,所以還是找不到數據。如果這時RDD8做過cache緩存,那麼就是在RDD8的時候進行了數據的保存並記錄了位置,這時如果RDD9失敗了就會從緩存中讀取RDD8的數據;如果RDD8沒有做cache就會找RDD7,以此類推,如果都沒有做cache就需要重新從HDFS中讀取數據。所以所謂的容錯就是指,當計算過程複雜,爲了降低因某些關鍵點計算出錯而需要重新計算的帶來的慘重代價的風險,則需要在某些關鍵點使用cache或用persist方法做一下緩存。

5 容錯

      上述緩存策略還存在一個問題。使用cache或persist的緩存策略是使用默認的僅在內存,所以實際的RDD緩存位置是在內存當中,如果機器出現問題,也會造成內存中的緩存RDD數據丟失。所以可以將要做容錯的RDD數據存到指定磁盤(可以是KDFS)路徑中,可以對RDD做doCheckpoint ()方法。使用doCheckpoint()方法的前提是需要在sc中預先設置SparkContext.setCheckpointDir(),設置數據存儲路徑。這時候如果程序計算過程出錯了,會先到cache中找緩存數據,如果cache中沒有就會到設置的磁盤路徑中找。

6 RDD特徵總結

6.1 關鍵特徵

(1)分佈式特徵
在這裏插入圖片描述
      RDD(Resilient Distributed Datasets)彈性分佈式數據集,是分佈式內存的一個抽象概念。我們可以抽象的代表對應一個HDFS上的文件,但是他實際上是被分區的,分爲多個分區撒落在Spark集羣中的不同節點上。比如現在我們的一個RDD有40萬條數據,並分爲4個partition,這4個分區數據分別存儲在集羣中的節點1、2、3、4中,而每個partition分到10萬條數據。如圖1所示,這樣的一個RDD將數據分佈式撒落在集羣的一批節點上,每個節點只是存儲RDD的部分partition,這就是RDD的分佈式結構模型。
(2)彈性式特徵
在這裏插入圖片描述
      當RDD的每個partition數據都存放到Spark集羣節點上時候,默認是都存放在內存中的,但是如果內存放不下這麼多的數據時,我們該怎麼辦呢?這時候RDD的彈性特徵就表現出來了。如上圖2所示,在節點3內存中最多隻能存儲6萬數據,結果我們需要存放一個partition數據爲10萬,那麼這時就得把partition中的剩餘4萬數據寫入到磁盤上進行保存了。而這種存儲的分配針對用戶是透明的。RDD的這種自動進行內存和磁盤之間權衡和卻換的機制,就是RDD的彈性特徵所在。
(3)容錯性特徵
      上面的內容已經講到,這裏不再進行贅述。

6.2 RDD特徵概要總結

(1)RDD是Spark提供的核心抽象,全稱爲Resilient Distributed Datasets,即彈性分佈式數據集。
(2)RDD在抽象上來說是一種元素集合,包含了數據。它是被分區的,分爲多個分區,每個分區分佈在集羣中的不同節點上,從而讓RDD中的數據可以被並行操作。
(3)RDD通常通過Hadoop上的文件,即HDFS文件或者Hive表,來進行創建;有時也可以通過應用程序中的集合來創建。
(4)RDD最重要的特性就是,提供了容錯性,可以自動從節點失敗中恢復過來。即如果某個節點上的RDD partition,因爲節點故障,導致數據丟了,那麼RDD會自動通過自己的數據來源重新計算該partition。這一切對使用者是透明的。
(5)RDD的數據默認情況下存放在內存中的,但是在內存資源不足時,Spark會自動將RDD數據寫入磁盤。

參考文章:
[1] https://www.cnblogs.com/adienhsuan/p/5654485.html
[2] https://blog.csdn.net/egraldloi/article/details/16343733
[3] https://blog.csdn.net/helloxiaozhe/article/details/78481784
[4] https://blog.csdn.net/huidoo_yang/article/details/79778693
[5] http://www.raincent.com/content-85-11254-1.html

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