Spark開發-RDD介面程式設計

Xlucas發表於2017-10-03

一般情況下面RDD包括5個介面

partition 分割槽,一個RDD會有一個或者多個分割槽
preferredLocations(P) 對於分割槽P而言,返回資料本地化計算的節點
dependencies() RDD的依賴關係
compute(p,context) 對於分割槽P而言,進行迭代計算
partitioner() RDD的分割槽函式

RDD分割槽 partitions

既然RDD是一個分割槽的資料集,那麼RDD肯定具備分割槽的屬性,對於一個RDD而言,分割槽的多少涉及對這個RDD進行平行計算的粒度,每一個RDD分割槽的計算操作都在一個單獨的任務中被執行,對於RDD的分割槽而言,使用者可以自行指定多少分割槽,如果沒有指定,那麼將會使用預設值,可以利用RDD的成員變數partitions所返回的partition陣列的大小來查詢一個RDD被劃分的分割槽數,我們通過parallelize來指定並行度

scala> val rdd=sc.parallelize(1 to 100,2)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:27

scala> rdd.partitions.size
res0: Int = 2

當然也可以在建立RDD的時間不指定分割槽,系統預設的數值是這個程式所分配到的資源的CPU核的個數

scala> val rdd=sc.parallelize(1 to 100)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[1] at parallelize at <console>:27

scala> rdd.partitions.size
res1: Int = 24

RDD優先位置(preferredLocations)

RDD優先位置屬性與spark中的排程相關,返回的是此RDD的每個partition所儲存的位置,按照“移動資料不如移動計算”的理念,在spark進行任務排程的時候,儘可能地將任務分配到資料塊所儲存的位置,以從Hadoop中讀取資料生成RDD為例,preferredLocation返回每一個資料塊所在的機器名或者IP地址,如果每一塊資料是多份儲存的,那麼就會返回多個機器地址.

在下面的程式中,首先通過sparkContext的textFile函式讀取一個13MB的檔案,生成一個型別為MappedRDD的rdd,然後通過rdd的依賴關係找到原始的HadoopRDD,HadoopRDD的partition的個數為2個,對於第一個partition而言,其preferredLocations返回了三個機器地址,以便後續排程的程式根據這個地址更加有效地分配任務.

scala> val rdd=sc.textFile("/data/wikiticker-2015-09-12-sampled.json")
17/10/03 22:57:11 INFO storage.MemoryStore: Block broadcast_0 stored as values in memory (estimated size 229.9 KB, free 2.7 GB)
17/10/03 22:57:12 INFO storage.MemoryStore: Block broadcast_0_piece0 stored as bytes in memory (estimated size 19.5 KB, free 2.7 GB)
17/10/03 22:57:12 INFO storage.BlockManagerInfo: Added broadcast_0_piece0 in memory on 192.168.18.140:52808 (size: 19.5 KB, free: 2.7 GB)
17/10/03 22:57:12 INFO spark.SparkContext: Created broadcast 0 from textFile at <console>:27
rdd: org.apache.spark.rdd.RDD[String] = /data/wikiticker-2015-09-12-sampled.json MapPartitionsRDD[3] at textFile at <console>:27

scala> val hadoopRDD=rdd.dependencies(0).rdd
hadoopRDD: org.apache.spark.rdd.RDD[_] = /data/wikiticker-2015-09-12-sampled.json HadoopRDD[2] at textFile at <console>:27

scala> hadoopRDD.partitions.size
17/10/03 22:59:19 INFO mapred.FileInputFormat: Total input paths to process : 1
res2: Int = 2

scala> hadoopRDD.preferredLocations(hadoopRDD.partitions(0))
res4: Seq[String] = ListBuffer(slave3, slave1, slave2)

RDD依賴關係(dependencies)

dependencies顧名思義就是依賴的意思,由於RDD是粗粒度的運算元據集,每一個轉換操作都會生成一個新的RDD,所以RDD之間就會形成類似於流水一樣的前後依賴關係,在spark中存在兩種型別的依賴,即窄依賴(Narrow dependencies)和寬依賴(wide dependencies)
1、窄依賴:每一個父RDD的分割槽最多隻被子RDD的一個分割槽所使用
2、寬依賴:多個子RDD的分割槽會依賴於同一個父RDD的分割槽,

下面這個圖很好的說明了窄依賴和寬依賴
這裡寫圖片描述

一個矩形表示一個RDD,在矩形中的橢圓形表示這個RDD的一個分割槽,例如 操作Map和Filter就會形成一個窄依賴,而沒有經過co-paritioned操作的兩個RDD資料集之間進行join操作就會形成寬依賴,在spark中需要明確地區分這兩種依賴關係有2個方面的原意,一:窄依賴可以在叢集的一個節點上如流水線一般的執行,可以計算所有父RDD的分割槽,相反的,寬依賴需要取得父RDD的所有分割槽上的資料進行計算,將會執行類似於MapReduce一樣的shuffle操作,二:對於窄依賴來說,節點計算失敗後的恢復會更加有效,只需要重新計算對應的父RDD的分割槽,而且可以在其他的節點上並行地計算,相反的,在有寬依賴的繼承關係中,一個節點的失敗將會導致其父RDD的多個分割槽重新計算,這個代價是非常高的,org.apache.spark.OneToOneDepengency窄依賴和org.apache.spark.ShuffleDependency寬依賴
scala> val rdd=sc.makeRDD(1 to 10)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[4] at makeRDD at :27

scala> val MapRDD=rdd.map(x=>(x,x))
MapRDD: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[5] at map at :29

scala> MapRDD.dependencies
res5: Seq[org.apache.spark.Dependency[_]] = List(org.apache.spark.OneToOneDependency@3c51d0a7)

scala> val shuffleRDD=MapRDD.partitionBy(new org.apache.spark.HashPartitioner(3))
shuffleRDD: org.apache.spark.rdd.RDD[(Int, Int)] = ShuffledRDD[6] at partitionBy at :31

scala> shuffleRDD.dependencies
res6: Seq[org.apache.spark.Dependency[_]] = List(org.apache.spark.ShuffleDependency@5cdcbeb5)

相關文章