RDD-API

一、創建RDD

1.由外部存儲系統的數據集創建由外部存儲系統的數據集創建,包括本地的文件系統,還有所有Hadoop支持的數據集,比如HDFS、Cassandra、HBase等

val rdd1 = sc.textFile("hdfs://node01:8020/wordcount/input/words.txt")

2.通過已有的RDD經過算子轉換生成新的RDD

val rdd2=rdd1.flatMap(_.split(" "))

3.由一個已經存在的Scala集合創建

val rdd3 = sc.parallelize(Array(1,2,3,4,5,6,7,8))

或者

val rdd4 = sc.makeRDD(List(1,2,3,4,5,6,7,8))

makeRDD方法底層調用了parallelize方法

二、RDD的方法/算子分類

RDD的算子分爲兩類:

1.Transformation轉換操作:返回一個新的RDD

2.Action動作操作:返回值不是RDD(無返回值或返回其他的)

RDD不實際存儲真正要計算的數據,而是記錄了數據的位置在哪裏,數據的轉換關係(調用了什麼方法,傳入什麼函數)

RDD中的所有轉換都是惰性求值/延遲執行的,也就是說並不會直接計算。只有當發生一個要求返回結果給Driver的Action動作時,這些轉換纔會真正運行

之所以使用惰性求值/延遲執行,是因爲這樣可以在Action時對RDD操作形成DAG有向無環圖進行Stage的劃分和並行優化,這種設計讓Spark更加有效率地運行。

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])

對rdd進行管道操作

coalesce(numPartitions) 

減少 RDD 的分區數到指定值。在過濾大量數據之後,可以執行此操作

repartition(numPartitions)

重新給 RDD 分區

Action動作算子

動作

含義

reduce(func)

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

collect()

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

count()

返回RDD的元素個數

first()

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

take(n)

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

takeSample(withReplacement,num, [seed])

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

takeOrdered(n, [ordering])

返回自然順序或者自定義順序的前 n 個元素

saveAsTextFile(path)

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

saveAsSequenceFile(path)

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

saveAsObjectFile(path)

將數據集的元素,以 Java 序列化的方式保存到指定的目錄下

countByKey()

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

foreach(func)

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

foreachPartition(func)

在數據集的每一個分區上,運行函數func

統計操作

算子

含義

count

個數

mean

均值

sum

求和

max

最大值

min

最小值

variance

方差

sampleVariance

從採樣中計算方差

stdev

標準差:衡量數據的離散程度

sampleStdev

採樣的標準差

stats

查看統計結果

三、常用算子操作:

集羣模式啓動

/export/servers/spark/sbin/start-all.sh

啓動spark-shell

/export/servers/spark/bin/spark-shell \

--master spark://node01:7077 \

--executor-memory 1g \

--total-executor-cores 2

本地模式啓動

/export/servers/spark/bin/spark-shell

1、創建RDD

val rdd1 = sc.parallelize(List(5,6,4,7,3,8,2,9,1,10))

val rdd2 = sc.makeRDD(List(5,6,4,7,3,8,2,9,1,10))

2、查看該RDD的分區數量

sc.parallelize(List(5,6,4,7,3,8,2,9,1,10)).partitions.length

//沒有指定分區數,默認值是2

sc.parallelize(List(5,6,4,7,3,8,2,9,1,10),3).partitions.length

//指定了分區數爲3

sc.textFile("hdfs://node01:8020/wordcount/input/words.txt").partitions.length

//2

RDD分區的數據取決於哪些因素?

RDD分區的原則是使得分區的個數儘量等於集羣中的CPU核心(core)數目,這樣可以充分利用CPU的計算資源,但是在實際中爲了更加充分的壓榨CPU的計算資源,會把並行度設置爲cpu核數的2~3倍。RDD分區數和啓動時指定的核數、調用方法時指定的分區數、如文件本身分區數 有關係

分區原則

1.啓動的時候指定的CPU核數確定了一個參數值:

spark.default.parallelism=指定的CPU核數(集羣模式最小2)

2.對於Scala集合調用parallelize(集合,分區數)方法,

如果沒有指定分區數,就使用spark.default.parallelism,

如果指定了就使用指定的分區數(不要指定大於spark.default.parallelism)

3.對於textFile(文件,分區數) defaultMinPartitions

如果沒有指定分區數sc.defaultMinPartitions=min(defaultParallelism,2)

如果指定了就使用指定的分區數sc.defaultMinPartitions=指定的分區數

rdd的分區數

對於本地文件:

rdd的分區數 = max(本地file的分片數,   sc.defaultMinPartitions)

對於HDFS文件:

rdd的分區數 = max(hdfs文件的block數目, sc.defaultMinPartitions)

所以如果分配的核數爲多個,且從文件中讀取數據創建RDD,即使hdfs文件只有1個切片,最後的Spark的RDD的partition數也有可能是2

3、map

對RDD中的每一個元素進行操作並返回操作的結果

//通過並行化生成rdd
val rdd1 = sc.parallelize(List(5, 6, 4, 7, 3, 8, 2, 9, 1, 10)) 

//對rdd1裏的每一個元素
rdd1.map(_ * 2).collect  //collect方法表示收集,是action操作

4、filter

函數中返回True的被留下,返回False的被過濾掉

val rdd2 = sc.parallelize(List(5, 6, 4, 7, 3, 8, 2, 9, 1, 10))
val rdd3 = rdd2.filter(_ >= 10)
rdd3.collect //10

5、flatmap

對RDD中的每一個元素進行先map再壓扁,最後返回操作的結果

val rdd1 = sc.parallelize(Array("a b c", "d e f", "h i j"))
//將rdd1裏面的每一個元素先切分再壓平
val rdd2 = rdd1.flatMap(_.split(' '))
rdd2.collect
//Array[String] = Array(a, b, c, d, e, f, h, i, j)

6、sortBy

val rdd1 = sc.parallelize(List(5, 6, 4, 7, 3, 8, 2, 9, 1, 10))
val rdd2 = rdd1.sortBy(x=>x,true) // x=>x 表示按照元素本身進行排序,True表示升序
rdd2.collect //1,2,3,.....

val rdd2 = rdd1.sortBy(x=>x+"",true)//x=>x+""表示按照x的字符串形式排序變成了字符串,結果爲字典順序
rdd2.collect//1,10,2,3...

7、交集、並集、差集、笛卡爾積

類型要一致

val rdd1 = sc.parallelize(List(5, 6, 4, 3))
val rdd2 = sc.parallelize(List(1, 2, 3, 4))

//union不會去重
val rdd3 = rdd1.union(rdd2)
rdd3.collect

//去重
rdd3.distinct.collect

//求交集
val rdd4 = rdd1.intersection(rdd2)
rdd4.collect

//求差集
val rdd5 = rdd1.subtract(rdd2)
rdd5.collect

//笛卡爾積
val rdd1 = sc.parallelize(List("jack", "tom"))//學生
val rdd2 = sc.parallelize(List("java", "python", "scala"))//課程
val rdd3 = rdd1.cartesian(rdd2)//表示所有學生的所有選課情況
rdd3.collect
//Array[(String, String)] = Array((jack,java), (jack,python), (jack,scala), (tom,java), (tom,python), (tom,scala))

8、join

join(內連接)聚合具有相同key組成的value元組

val rdd1 = sc.parallelize(List(("tom", 1), ("jerry", 2), ("kitty", 3)))
val rdd2 = sc.parallelize(List(("jerry", 9), ("tom", 8), ("shuke", 7), ("tom", 2)))
val rdd3 = rdd1.join(rdd2)
rdd3.collect
//Array[(String, (Int, Int))] = Array((tom,(1,8)), (tom,(1,2)), (jerry,(2,9)))

val rdd4 = rdd1.leftOuterJoin(rdd2) //左外連接,左邊的全留下,右邊的滿足條件的才留下
rdd4.collect
//Array[(String, (Int, Option[Int]))] = Array((tom,(1,Some(2))), (tom,(1,Some(8))), (jerry,(2,Some(9))), (kitty,(3,None)))

val rdd5 = rdd1.rightOuterJoin(rdd2)  
rdd5.collect
//Array[(String, (Option[Int], Int))] = Array((tom,(Some(1),2)), (tom,(Some(1),8)), (jerry,(Some(2),9)), (shuke,(None,7)))

val rdd6 = rdd1.union(rdd2)
rdd6.collect
//Array[(String, Int)] = Array((tom,1), (jerry,2), (kitty,3), (jerry,9), (tom,8), (shuke,7), (tom,2))

9、groupbykey

groupByKey()的功能是,對具有相同鍵的值進行分組。
比如,對四個鍵值對("spark",1)、("spark",2)、("hadoop",3)和("hadoop",5),
採用groupByKey()後得到的結果是:("spark",(1,2))和("hadoop",(3,5))。

//按key進行分組
val rdd6 = sc.parallelize(Array(("tom",1), ("jerry",2), ("kitty",3), ("jerry",9), ("tom",8), ("shuke",7), ("tom",2)))
val rdd7=rdd6.groupByKey
rdd7.collect
//Array[(String, Iterable[Int])] = Array((tom,CompactBuffer(1, 8, 2)), (jerry,CompactBuffer(2, 9)), (shuke,CompactBuffer(7)), (kitty,CompactBuffer(3)))

10、cogroup

cogroup是RDD內部分組,RDD之間分組

val rdd1 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
val rdd3 = rdd1.cogroup(rdd2)
rdd3.collect
// Array((tom,(CompactBuffer(1, 2),CompactBuffer(1))), (jerry,(CompactBuffer(3),CompactBuffer(2))), (shuke,(CompactBuffer(),CompactBuffer(2))), (kitty,(CompactBuffer(2),CompactBuffer())))

11、groupBy

根據指定的函數中的規則/key進行分組

val intRdd = sc.parallelize(List(1,2,3,4,5,6))
val result = intRdd.groupBy(x=>{if(x%2 == 0)"even" else "odd"}).collect
//Array[(String, Iterable[Int])] = Array((even,CompactBuffer(2, 4, 6)), (odd,CompactBuffer(1, 3, 5)))

12、reduce

val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5))
//reduce聚合
val result = rdd1.reduce(_ + _) 
//  第一_ 上次一個運算的結果,第二個_ 這一次進來的元素

reduceByKeyTransformation還是Action? --Transformation

reduceTransformation還是Action? --Action

13、reducebykey

reducebykey是轉換算子

reduceByKey(func)的功能是,使用func函數合併具有相同鍵的值。
比如,reduceByKey((a,b) => a+b),有四個鍵值對("spark",1)、("spark",2)、("hadoop",3)和("hadoop",5)
對具有相同key的鍵值對進行合併後的結果就是:("spark",3)、("hadoop",8)。
可以看出,(a,b) => a+b這個Lamda表達式中,a和b都是指value,
比如,對於兩個具有相同key的鍵值對("spark",1)、("spark",2),a就是1,b就是2。

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) //並集
rdd3.collect
//Array[(String, Int)] = Array((tom,1), (jerry,3), (kitty,2), (shuke,1), (jerry,2), (tom,3), (shuke,2), (kitty,5))

//按key進行聚合
val rdd4 = rdd3.reduceByKey(_ + _)
rdd4.collect
//Array[(String, Int)] = Array((tom,4), (jerry,5), (shuke,3), (kitty,7))

14、repartition

改變分區數
val rdd1 = sc.parallelize(1 to 10,3) //指定3個分區
//利用repartition改變rdd1分區數
//減少分區
rdd1.repartition(2).partitions.length //新生成的rdd分區數爲2
rdd1.partitions.length //3 //注意:原來的rdd分區數不變
//增加分區
rdd1.repartition(4).partitions.length
//減少分區
rdd1.repartition(3).partitions.length
//利用coalesce改變rdd1分區數
//減少分區
rdd1.coalesce(2).partitions.size
rdd1.coalesce(4).partitions.size

repartition可以增加和減少rdd中的分區數,

coalesce默認減少rdd分區數,增加rdd分區數不會生效。

不管增加還是減少分區數原rdd分區數不變,變的是新生成的rdd的分區數

15、collect

val rdd1 = sc.parallelize(List(6,1,2,3,4,5), 2)

rdd1.collect

16、count

count統計集合中元素的個數
rdd1.count //6

求RDD中最外層集合裏面的元素的個數
val rdd3 = sc.parallelize(List(List("a b c", "a b b"),List("e f g", "a f g"), List("h i j", "a a b")))
rdd3.count //3

17、distinct

val rdd = sc.parallelize(Array(1,2,3,4,5,5,6,7,8,1,2,3,4), 3)
rdd.distinct.collect

18、top

//取出最大的前N個
val rdd1 = sc.parallelize(List(3,6,1,2,4,5))
rdd1.top(2)

19、take

//按照原來的順序取前N個
rdd1.take(2) //3 6
//需求:取出最小的2個
rdd1.sortBy(x=>x,true).take(2)

20、first

//按照原來的順序取前第一個
rdd1.first

21、keys、values

val rdd1 = sc.parallelize(List("dog", "tiger", "lion", "cat", "panther", "eagle"), 2)
val rdd2 = rdd1.map(x => (x.length, x))
rdd2.collect
//Array[(Int, String)] = Array((3,dog), (5,tiger), (4,lion), (3,cat), (7,panther), (5,eagle))
rdd2.keys.collect
//Array[Int] = Array(3, 5, 4, 3, 7, 5)
rdd2.values.collect
//Array[String] = Array(dog, tiger, lion, cat, panther, eagle)

22、mapValues

mapValues表示對RDD中的元素進行操作,Key不變,Value變爲操作之後
val rdd1 = sc.parallelize(List((1,10),(2,20),(3,30)))
val rdd2 = rdd1.mapValues(_*2).collect //_表示每一個value ,key不變,將函數作用於value
//(1,20),(2,40),(3,60)

23、collectAsMap

轉換成Map
val rdd = sc.parallelize(List(("a", 1), ("b", 2)))
rdd.collectAsMap
//scala.collection.Map[String,Int] = Map(b -> 2, a -> 1)

24、foreach和foreachPartition

val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)
rdd1.foreach(x => println(x*100)) //x是每一個元素 
rdd1.foreachPartition(x => println(x.reduce(_ + _)))  //x是每個分區

注意:foreach和foreachPartition都是Action操作,但是以上代碼在spark-shell中執行看不到輸出結果,

原因是傳給foreach和foreachPartition的計算函數是在各個分區執行的,即在集羣中的各個Worker上執行的

應用場景:

比如在函數中要將RDD中的元素保存到數據庫

foreach:會將函數作用到RDD中的每一條數據,那麼有多少條數據,操作數據庫連接的開啓關閉就得執行多少次

foreachPartition:將函數作用到每一個分區,那麼每一個分區執行一次數據庫連接的開啓關閉,有幾個分區就會執行數據庫連接開啓關閉

import org.apache.spark.{SparkConf, SparkContext}

object Test {
  def main(args: Array[String]): Unit = {
    val config = new SparkConf().setMaster("local[*]").setAppName("WordCount")
    val sc = new SparkContext(config)
    //設置日誌輸出級別
    sc.setLogLevel("WARN")
    val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)
    //Applies a function f to all elements of this RDD.
    //將函數f應用於此RDD的所有元素
    rdd1.foreach(x => println(x*100))   
//把函數傳給各個分區,在分區內循環遍歷該分區中的元素 
//x每個元素,即一個一個的數字
    println("==========================")
    //Applies a function f to each partition of this RDD.
    //將函數f應用於此RDD的每個分區
    rdd1.foreachPartition(x => println(x.reduce(_ + _))) 
//把各個分區傳遞給函數執行 
//x是每個分區
  }
}

25、map和mapPartitions

將每一個分區傳遞給函數
val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)
rdd1.mapPartitions(x=>x.map(y=>y*2)).collect //x是每一個分區,y是分區中的元素

26、mapPartitionsWithIndex(同時獲取分區號)

功能:取分區中對應的數據時,還可以將分區的編號取出來,這樣就可以知道數據是屬於哪個分區的

val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 3)
//該函數的功能是將對應分區中的數據取出來,並且帶上分區編號
// 一個index 分區編號
// 一個iter分區內的數據
val func = (index: Int, iter: Iterator[Int]) => {
  iter.map(x => "[partID:" +  index + ", val: " + x + "]")
}

rdd1.mapPartitionsWithIndex(func).collect

//Array[String] = Array(
[partID:0, val: 1], [partID:0, val: 2], [partID:0, val: 3], 
[partID:1, val: 4], [partID:1, val: 5], [partID:1, val: 6],
[partID:2, val: 7], [partID:2, val: 8], [partID:2, val: 9]
)

27、aggregate

val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 3)

//0表示初始值

//第一個_+_,表示區內聚合,第一個_表示歷史值,第二個_表示當前值

//第二個_+_,表示區間聚合,第一個_表示歷史值,第二個_表示當前值

val result1: Int = rdd1.aggregate(0)(_+_,_+_)  //45  ==> 6 + 15 + 24 = 45

//10表示初始值,每個分區有初始值,區間聚合的時候也有初始值

val result2: Int = rdd1.aggregate(10)(_+_,_+_) //85  ==> 10+ (10+6 + 10+15 + 10+24)=85

28、combineByKey

val rdd1 = sc.textFile("hdfs://node01:8020/wordcount/input/words.txt").flatMap(_.split(" ")).map((_, 1))
//Array((hello,1), (me,1), (hello,1), (you,1), (hello,1), (her,1))

//x => x,表示key不變
//(a: Int, b: Int) => a + b:表示區內聚合
//(m: Int, n: Int) => m + n:表示區間聚合
val rdd2 = rdd1.combineByKey(x => x, (a: Int, b: Int) => a + b, (m: Int, n: Int) => m + n)
//val rdd2 = rdd1.combineByKey(x => x, _+_, _+_)//注意這裏簡寫錯誤,原則:能省則省,不能省則不要偷懶

rdd2.collect
//Array[(String, Int)] = Array((hello,3), (me,1), (you,1), (her,1))


val rddData1: RDD[(String, Float)] = sc.parallelize(
      Array(
        ("班級1", 95f),
        ("班級2", 80f),
        ("班級1", 75f),
        ("班級3", 97f),
        ("班級2", 88f)),
      2)

val rddData2 = rddData1.combineByKey(
      grade => (grade, 1),
      (gc: (Float, Int), grade) => (gc._1 + grade, gc._2 + 1),
      (gc1: (Float, Int), gc2: (Float, Int)) => (gc1._1 + gc2._1, gc1._2 + gc2._2)
    )

val rddData3 = rddData2.map(t => (t._1, t._2._1 / t._2._2))
rddData3.collect

29、aggregateByKey

val pairRDD = sc.parallelize(List( ("cat",2), ("cat", 5), ("mouse", 4),("cat", 12), ("dog", 12), ("mouse", 2)), 2)

def func(index: Int, iter: Iterator[(String, Int)]) : Iterator[String] = {
  iter.map(x => "[partID:" +  index + ", val: " + x + "]")
}
pairRDD.mapPartitionsWithIndex(func).collect
//Array(
[partID:0, val: (cat,2)], [partID:0, val: (cat,5)], [partID:0, val: (mouse,4)], 
[partID:1, val: (cat,12)], [partID:1, val: (dog,12)], [partID:1, val: (mouse,2)]
)

pairRDD.aggregateByKey(0)(math.max(_, _), _ + _).collect 
// Array[(String, Int)] = Array((dog,12), (cat,17), (mouse,6))
//100表示區內初始值,區間聚合沒有
pairRDD.aggregateByKey(100)(math.max(_, _), _ + _).collect
//Array[(String, Int)] = Array((dog,100), (cat,200), (mouse,200))  

pairRDD.aggregateByKey(5)(math.max(_, _), _ + _).collect
//Array[(String, Int)] = Array((dog,12), (cat,17), (mouse,10))

pairRDD.aggregateByKey(10)(math.max(_, _), _ + _).collect
//Array[(String, Int)] = Array((dog,12), (cat,22), (mouse,20))


val rddData1 = sc.parallelize(
      Array(
        ("用戶1", "接口1"),
        ("用戶2", "接口1"),
        ("用戶1", "接口1"),
        ("用戶1", "接口2"),
        ("用戶2", "接口3")),
      2)
val rddData2 = rddData1.aggregateByKey(collection.mutable.Set[String]())(
      (urlSet, url) => urlSet += url,
      (urlSet1, urlSet2) => urlSet1 ++= urlSet2)
rddData2.collect

30、小練習

給定一個鍵值對RDD

val rdd = sc.parallelize(Array(("spark",2),("hadoop",6),("hadoop",4),("spark",6)))

key表示圖書名稱,

value表示某天圖書銷量,

請計算每個鍵對應的平均值,也就是計算每種圖書的每天平均銷量。

最終結果:("spark",4),("hadoop",5)

val rdd1 = rdd.groupByKey
rdd1.collect
//Array((spark,CompactBuffer(6, 2)), (hadoop,CompactBuffer(4, 6)))
val rdd2 = rdd1.mapValues(v => v.sum / v.size)
rdd2.collect

答案

val rdd = sc.parallelize(Array(("spark",2),("hadoop",6),("hadoop",4),("spark",6)))
val rdd2 = rdd.groupByKey()
rdd2.collect
//Array[(String, Iterable[Int])] = Array((spark,CompactBuffer(2, 6)), (hadoop,CompactBuffer(6, 4)))

val rdd3 = rdd2.map(t=>(t._1,t._2.sum /t._2.size))
rdd3.collect
//Array[(String, Int)] = Array((spark,4), (hadoop,5))

四、總結

分類

RDD的算子分爲兩類,一類是Transformation轉換操作,一類是Action動作操作

 

●如何區分Transformation和Action

返回值是RDD的爲Transformation轉換操作,延遲執行/懶執行/惰性執行

返回值不是RDD(UnitArrayInt)的爲Action動作操作

 

●面試題:

1.Transformation操作的API有哪些? --map/flatMap/filter....

2.Action操作的API有哪些? --collect/reduce/saveAsTextFile....

3.reduceByKey是Transformation還是Action? --Transformation

4.reduce是Transformation還是Action? -- Action

5.foreach和foreachPartition的區別? foreach作用於每個元素,foreachPartition作用於每個分區

 

●注意:

RDD不實際存儲真正要計算的數據,而只是記錄了RDD的轉換關係(調用了什麼方法,傳入什麼函數,依賴哪些RDD,分區器是什麼,數量塊來源機器列表)

RDD中的所有轉換操作都是延遲執行(懶執行)的,也就是說並不會直接計算。只有當發生Action操作的時候,這些轉換纔會真正運行。

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