Spark:RDD簡介及基礎算子

本文主要介紹Spark Core的核心內容:RDD。包含以下章節和對應的內容

章節 內容
1 RDD簡介
2 RDD分區
3 RDD的依賴關係
4 RDD的緩存機制和區別
5 RDD創建的兩種方式
6 RDD算子和總結
7 RDD 算子操作案例

 

1、RDD簡介

RDD(Resilient Distributed Datasets,彈性分佈式數據集),是Spark最爲核心的概念,自然也是理解Apache Spark 工作原理的最佳入口之一。

RDD的特點:

  1. 是一個分區的只讀記錄的集合;
  2. 一個具有容錯機制的特殊集;
  3. 只能通過在穩定的存儲器或其他RDD上的確定性操作(轉換)來創建;
  4. 可以分佈在集羣的節點上,以函數式操作集合的方式,進行各種並行操作

RDD之所以爲“彈性”的特點

  1. 基於Lineage的高效容錯(第n個節點出錯,會從第n-1個節點恢復,血統容錯);
  2. Task如果失敗會自動進行特定次數的重試(默認4次);
  3. Stage如果失敗會自動進行特定次數的重試(可以值運行計算失敗的階段),只計算失敗的數據分片;
  4. 數據調度彈性:DAG TASK 和資源管理無關;
  5. checkpoint;
  6. 自動的進行內存和磁盤數據存儲的切換

2、RDD的分區

首先,RDD是一個邏輯概念,分區是一個物理概念,並且帶有下標

舉例:通過SparkContext.parallelize創建一個RDD: 

val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8),3)      # 後面的數字3 表示3個分區

也就是說,RDD 內部的數據集合在邏輯上(以及物理上)被劃分成多個小集合,這樣的每一個小集合被稱爲分區。

那麼引出兩個問題:

  1. 如何查看分區運行在哪個Worker(機器)上?
  2. 如何查看每個分區中的數據?

針對問題1和2: 在源碼級別,RDD 類內存儲一個 Partition 列表。每個 Partition 對象都包含一個 index 成員,通過 RDD 編號 + index 就能從唯一確定分區的 Block 編號,持久化的 RDD 就能通過這個 Block 編號從存儲介質中獲得對應的分區數據。

 

3、RDD的依賴關係

RDD和它依賴的parent RDD(s)的關係有兩種不同的類型,即窄依賴(narrow dependency)和寬依賴(wide dependency)。

  1. 窄依賴指的是每一個parent RDD的Partition最多被子RDD的一個Partition使用
  2. 寬依賴指的是多個子RDD的Partition會依賴同一個parent RDD的Partition

 

4、RDD的緩存機制和區別

RDD緩存機制有兩種,cache和pesist,兩種區別如下:

  1. cache和persist都是用於將一個RDD進行緩存的,這樣在之後使用的過程中就不需要重新計算了,可以大大節省程序運行時間;
  2.  cache只有一個默認的緩存級別MEMORY_ONLY ,cache調用了persist,而persist可以根據情況設置其它的緩存級別;3)
  3. executor執行的時候,默認60%做cache,40%做task操作,persist最根本的函數,最底層的函數

5、RDD創建的兩種方式

  1:通過SparkContext.parallelize創建
val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8),3)    # 3個分區


  2:通過讀取外部的數據源創建:比如:HDFS、本地目錄

val rdd1 = sc.textFile("hdfs://bigdata:9000/input/data.txt")    # HDFS文件
val rdd2 = sc.textFile("/root/temp/data.txt")   # 本地文件

 

6、RDD算子

RDD中的所有轉換都是延遲加載的,也就是說,它們並不會直接計算結果。相反的,它們只是記住這些應用到基礎數據集(例如一個文件)上的轉換動作。只有當發生一個要求返回結果給Driver的動作時,這些轉換纔會真正運行。這種設計讓Spark更加有效率地運行。

從大方向來說:RDD算子分爲Transformation算子 Action 算子,其中Transformation 操作是延遲計算的,也就是說從一個RDD 轉換生成另一個 RDD 的轉換操作不是馬上執行,需要等到有 Action 操作的時候纔會真正觸發運算。Action 算子會觸發 SparkContext 提交 Job 作業,並將數據輸出 Spark系統

從小方向來說:RDD 算子大致可以分爲以下三類:

  1. Value數據類型的Transformation算子,這種變換並不觸發提交作業,針對處理的數據項是Value型的數據。
  2. Key-Value數據類型的Transfromation算子,這種變換並不觸發提交 作業,針對處理的數據項是Key-Value型的數據對。
  3. Action算子,這類算子會觸發SparkContext提交Job作業

更多詳細算子介紹可以參考:Scala官方API

以下表格是關於Transformation算子-會延時加載(計算)

轉換

含義

map(func)

返回一個新的RDD,該RDD由每一個輸入元素經過func函數轉換後組成

filter(func)

返回一個新的RDD,該RDD由經過func函數計算後返回值爲true的輸入元素組成

flatMap(func)

類似於map,但是每一個輸入元素可以被映射爲0或多個輸出元素(所以func應該返回一個序列,而不是單一元素)

mapPartitions(func)

類似於map,但獨立地在RDD的每一個分片上運行,因此在類型爲T的RDD上運行時,func的函數類型必須是Iterator[T] => Iterator[U]

mapPartitionsWithIndex(func)

類似於mapPartitions,但func帶有一個整數參數表示分片的索引值,因此在類型爲T的RDD上運行時,func的函數類型必須是(Int, Interator[T]) => Iterator[U]

sample(withReplacement, fraction, seed)

根據fraction指定的比例對數據進行採樣,可以選擇是否使用隨機數進行替換,seed用於指定隨機數生成器種子

union(otherDataset)

對源RDD和參數RDD求並集後返回一個新的RDD

intersection(otherDataset)

對源RDD和參數RDD求交集後返回一個新的RDD

distinct([numTasks]))

對源RDD進行去重後返回一個新的RDD

groupByKey([numTasks])   

在一個(K,V)的RDD上調用,返回一個(K, Iterator[V])的RDD

reduceByKey(func, [numTasks])

在一個(K,V)的RDD上調用,返回一個(K,V)的RDD,使用指定的reduce函數,將相同key的值聚合到一起,與groupByKey類似,reduce任務的個數可以通過第二個可選的參數來設置

aggregateByKey(zeroValue)(seqOp,combOp,[numTasks])

 

sortByKey([ascending], [numTasks])

在一個(K,V)的RDD上調用,K必須實現Ordered接口,返回一個按照key進行排序的(K,V)的RDD

sortBy(func,[ascending], [numTasks])

與sortByKey類似,但是更靈活

join(otherDataset, [numTasks])

在類型爲(K,V)和(K,W)的RDD上調用,返回一個相同key對應的所有元素對在一起的(K,(V,W))的RDD

cogroup(otherDataset, [numTasks])

在類型爲(K,V)和(K,W)的RDD上調用,返回一個(K,(Iterable<V>,Iterable<W>))類型的RDD

cartesian(otherDataset)

笛卡爾積

pipe(command, [envVars])

 

coalesce(numPartitions)    

 

repartition(numPartitions)

 

repartitionAndSortWithinPartitions(partitioner)

 

 

以下表格是關於Action算子- 觸發計算

動作

含義

reduce(func)

通過func函數聚集RDD中的所有元素,這個功能必須是課交換且可並聯的

collect()

在驅動程序中,以數組的形式返回數據集的所有元素

count()

返回RDD的元素個數

first()

返回RDD的第一個元素(類似於take(1))

take(n)

返回一個由數據集的前n個元素組成的數組

takeSample(withReplacement,num, [seed])

返回一個數組,該數組由從數據集中隨機採樣的num個元素組成,可以選擇是否用隨機數替換不足的部分,seed用於指定隨機數生成器種子

takeOrdered(n[ordering])

 

saveAsTextFile(path)

將數據集的元素以textfile的形式保存到HDFS文件系統或者其他支持的文件系統,對於每個元素,Spark將會調用toString方法,將它裝換爲文件中的文本

saveAsSequenceFile(path

將數據集中的元素以Hadoop sequencefile的格式保存到指定的目錄下,可以使HDFS或者其他Hadoop支持的文件系統。

saveAsObjectFile(path

 

countByKey()

針對(K,V)類型的RDD,返回一個(K,Int)的map,表示每一個key對應的元素個數。

foreach(func)

在數據集的每一個元素上,運行函數func進行更新。

 

7、RDD 算子操作案例:

案例1:映射和過濾

創建一個RDD,也可以使用List
val rdd1 = sc.parallelize(List(5,6,7,8,1,2,3,100,30))
	
每個元素乘以2,再排序
val rdd2 = rdd1.map(_*2).sortBy(x=>x,true) #降序
	
過濾出大於20的元素
val rdd3 = rdd2.filter(_ > 20)

輸出結果
rdd3.collect

 

案例2:字符串操作和字符計數

val rdd4 = sc.parallelize(Array("a b c","d b c","a y c"))

val rdd5 = rdd4.flatMap(_.split(" "))  # 切分字符並切平

val rdd6 = rdd5.map((_,1)) # 逐一統計

val rdd7 = rdd6.reduceByKey(_+_).collect # 計數輸出結果

 

 

案例3:集合操作

集合運算、去重
val rdd6 = sc.parallelize(List(5,6,7,8,1,2,3,100,30))
val rdd7 = sc.parallelize(List(1,2,3,4,5,6,7,8,9,10))
	
並集:union(如果是SQL中的集合運算,對集合是有要求的)
val rdd8 = rdd6.union(rdd7)
去重
rdd8.distinct.collect
	
交集: intersect(如果是SQL中的集合運算,對集合是有要求的)
val rdd9 = rdd6.intersection(rdd7)

 

 

案例4:key-value操作

val rdd1 = sc.parallelize(List(("tom", 1), ("jerry", 3), ("kitty", 2),  ("shuke", 1)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 3), ("shuke", 2), ("kitty", 5)))
val rdd3 = rdd1.union(rdd2)
//按key進行聚合
val rdd4 = rdd3.reduceByKey(_ + _)
rdd4.collect
//按value的降序排序
val rdd5 = rdd4.map(t => (t._2, t._1)).sortByKey(false).map(t => (t._2, t._1))
rdd5.collect

提示:交換了key-value的位置,並且交換了兩次

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