spark學習(10)-RDD的介紹和常用算子

RDD(彈性分佈式數據集,裏面並不存儲真正要計算的數據,你對RDD的操作,
他會在Driver端轉換成Task,下發到Executor計算分散在多臺集羣上的數據)


RDD是一個代理,你對代理進行操作,他會生成Task,幫你計算
你操作這個代理,就像操作本地集合一樣,不用關心任務調度,容錯等


val r1 = sc.textFile("hdfs://hdp-02:9000/wc")

r1.count //這樣就統計出有多少行

創建RDD的方式
生成一個RDD
sc.textFile("hdfs://hdp-01:9000/wc")
源代碼還是hadoop的API
RDD是一個基本的抽象
RDD的算子一類是Transformation(Lazy)
一種是Action(觸發任務執行)

創建方式
1。通過外部的儲存系統創建RDD
val rdd1 = sc.textFile("hdfs://hdp-01:9000/wc")
2.將Driver的scala集合通過並行化的方式編程RDD(學習,實驗
val arr = Array(1,2,3,4,5,6)
val rdd2 = sc.parallelize(arr) //此時rdd2爲集合轉化成的一個RDD
3.條用一個已經存在的RDD的Transformation,生成一個新的RDD
val rdd3 = rdd.map(_ * 10))//這個rdd3就是通過rdd2轉化而得到的

 

 

#RDD中的常用算子(方法) 
#常用Transformation(即轉換,延遲加載)

//特點是1.lazy
//2.生成新的RDD 
#通過並行化scala集合創建RDD
val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8))
#查看該rdd的分區數量
rdd1.partitions.length


val rdd1 = sc.parallelize(List(5,6,4,7,3,8,2,9,1,10))//把list轉化成rdd 
val rdd2 = sc.parallelize(List(5,6,4,7,3,8,2,9,1,10)).map(_*2).sortBy(x=>x,true)//按照本身的數字排序 
val rdd3 = rdd2.filter(_>10) //過濾出rdd2中大於10的 
val rdd2 = sc.parallelize(List(5,6,4,7,3,8,2,9,1,10)).map(_*2).sortBy(x=>x+"",true)//傳進來的排序規則是按照字符串排序的,下同 
val rdd2 = sc.parallelize(List(5,6,4,7,3,8,2,9,1,10)).map(_*2).sortBy(x=>x.toString,true)


val rdd4 = sc.parallelize(Array("a b c", "d e f", "h i j"))
rdd4.flatMap(_.split(' ')).collect

val rdd5 = sc.parallelize(List(List("a b c", "a b b"),List("e f g", "a f g"), List("h i j", "a a b")))


List("a b c", "a b b") =List("a","b",))

rdd5.flatMap(_.flatMap(_.split(" "))).collect

#union求並集,注意類型要一致
val rdd6 = sc.parallelize(List(5,6,4,7))
val rdd7 = sc.parallelize(List(1,2,3,4))
val rdd8 = rdd6.union(rdd7)//將兩個Rdd合成一個rdd 不去重 
rdd8.distinct.sortBy(x=>x).collect

#intersection求交集
val rdd9 = rdd6.intersection(rdd7) //將兩個rdd合成一個交集的rdd 

#join(連接)
val rdd11 = sc.parallelize(List(("tom", 1), ("jerry", 2), ("kitty", 3)))
val rdd12 = sc.parallelize(List(("jerry", 9), ("tom", 8), ("shuke", 7), ("tom", 2)))


val rdd13 = rdd11.join(rdd12)
//結果爲Array[(String, (Int, Int))] = Array((tom,(1,8)), (tom,(1,2)), (jerry,(2,9))) 
val rdd13 = rdd11.leftOuterJoin(rdd12)
//結果爲Array[(String, (Int, Option[Int]))] = Array((tom,(1,Some(8))), (tom,(1,Some(2))), (kitty,(3,None)), (jerry,(2,Some(9)))) 
val rdd13 = rdd11.rightOuterJoin(rdd12)
//結果爲Array[(String, (Option[Int], Int))] = Array((tom,(Some(1),8)), (tom,(Some(1),2)), (shuke,(None,7)), (jerry,(Some(2),9))) 


#groupByKey
val rdd3 = rdd1 union rdd2
rdd3.groupByKey
//Array[(String, Iterable[Int])] = Array((tom,CompactBuffer(8, 2, 1)), (shuke,CompactBuffer(7)), (kitty,CompactBuffer(3)), (jerry,CompactBuffer(2, 9)))
rdd3.groupByKey.map(x=>(x._1,x._2.sum))//結果與下同 
rdd3.groupByKey.mapValues(_.sum).collect
//Array[(String, Int)] = Array((tom,11), (shuke,7), (kitty,3), (jerry,11))



#WordCount

//reduceByKey是先局部聚合  然後shuffle,shuffle的次數少,然後groupByKey再map的話是要把很多(單詞,1)shuffle到一臺機器上再聚合,這樣shuffle的次數會很多
sc.textFile("/root/words.txt").flatMap(x=>x.split(" ")).map((_,1)).reduceByKey(_+_).sortBy(_._2,false).collect
sc.textFile("/root/words.txt").flatMap(x=>x.split(" ")).map((_,1)).groupByKey.map(t=>(t._1, t._2.sum)).collect

#cogroup
val rdd1 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
//Array[(String, (Iterable[Int], Iterable[Int]))] = Array((tom,(CompactBuffer(1, 2),CompactBuffer(1))), \
//(shuke,(CompactBuffer(),CompactBuffer(2))), (kitty,(CompactBuffer(2),CompactBuffer())), (jerry,(CompactBuffer(3),CompactBuffer(2)))) 
val rdd3 = rdd1.cogroup(rdd2)
val rdd4 = rdd3.map(t=>(t._1, t._2._1.sum + t._2._2.sum))

#cartesian笛卡爾積
val rdd1 = sc.parallelize(List("tom", "jerry"))
val rdd2 = sc.parallelize(List("tom", "kitty", "shuke"))
val rdd3 = rdd1.cartesian(rdd2)

###################################################################################################

#spark action
#後面2位一個分區 
#創建幾個分區就會生成幾個task,這幾個task會將數據進行處理然後寫入到hdfs
#幾個task就會生成幾個文件 
#如果沒有指定的話,那麼task的數據跟啓動時設置的core的數量一樣 
#但並不是越多越好比如:你電腦上可以有8個線程同時運行,但你可以在你電腦上啓動80個線程,他對於這80個線程是每8個線程作爲一個批次進行切換的 
val rdd1 = sc.parallelize(List(1,2,3,4,5), 2)

#collect
rdd1.collect

#reduce
val r = rdd1.reduce(_+_)

#count
rdd1.count

#top
rdd1.top(2)

#take
rdd1.take(2)

#first(similer to take(1))
rdd1.first

#takeOrdered
rdd1.takeOrdered(3)



#查看一個rdd有幾個分區 
rdd1.partitions.length
#rdd最小的分區數量是2 

#這樣會先把test下的左右文件大小加在一起, 然後再重新計算應該分區的大小 
#觸發Action的時候纔會指定有幾個分區  
sc.textFile("hdfs://hdp-01:9099/test)
 
mapPartitionsWithIndex
一次拿出一個分區(分區中並沒有數據,而是記錄堯都區那些數據,真正生成的Task弧度去多條數據)
,並且可以將分區的編號取出來,
功能:取出分區中對應的數據時,還可以將分區的編號取出來,知道數據是屬於那個分區 
    //該函數的功能是將對應分區中的數據取出來,並且帶上分區編號
    val func = (index: Int, it: Iterator[Int]) => {
      it.map(e => s"part: $index, ele: $e")
    }
val rdd1 = rdd.mapPartitionsWithIndex(func)


aggregate方法
val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 3)
rdd1.aggregate(0)(_+_,_+_) //結果爲45,第一個括號是初始值,第二個括號表達先是對每個分區的操作,再總的操作 
//下面這個意思是先求出每個分區的最大值,然後加起來 
rdd1.aggregate(0)(math.max(_,_), _+_)

#結果是25第一個分區1,2,3,和初始值比結果爲5,第二個分區4,5,6,結果爲6,第三個爲9,這三個最大值加起來,再加上一個初始值 
rdd1.aggregate(5)(math.max(_._), _+_) 


val pairRDD = sc.parallelize(List( ("cat",2), ("cat", 5), ("mouse", 4),("cat", 12), ("dog", 12), ("mouse", 2)), 2)
def func2(index: Int, iter: Iterator[(String, Int)]) : Iterator[String] = {
  iter.map(x => "[partID:" +  index + ", val: " + x + "]")
}
pairRDD.mapPartitionsWithIndex(func2).collect
pairRDD.aggregateByKey(0)(math.max(_, _), _ + _).collect
// Array[(String, Int)] = Array((dog,112), (cat,219), (mouse,206)) 因爲cat在第一個分區加了100,在第二個分區也加了100,所以加了200,dog只在一個分區裏出現,加了100 
pairRDD.aggregateByKey(100)(_+_, _ + _).collect


collection.map
var rdd = sc.parallelize(List(("a",1), ("b",2))) 

rdd.mapValues(_*100)

rdd.mapValues(_*100).collectAsMap


collect的執行過程
rdd執行action的方法之後,會從後往前推
知道找到一個數據源,有幾個分區就會在driver端生成幾個task ,然後發送給worker
worker會找到數據源,下載數據,邊下載邊計算,
計算完之後會發送給driver端(如果數據太大的話,不要把數據收集到driver端,在寫入數據庫啥的,這樣driver是一個瓶頸,直接把計算結果寫入到數據庫或者啥的) 

countByKey 

val rdd1 = sc.parallelize(List(("a", 1), ("b", 2), ("b", 2), ("c", 2), ("c", 1)))
rdd1.countByKey //scala.collection.Map[String,Long] = Map(a -> 1, b -> 2, c -> 2) 
rdd1.countByValue//scala.collection.Map[(String, Int),Long] = Map((b,2) -> 2, (c,2) -> 1, (c,1) -> 1, (a,1) -> 1)

filterByRange

val rdd1 = sc.parallelize(List(("e", 5),("b", 5),("c", 3), ("d", 4), ("c", 2), ("a", 1)))
val rdd2 = rdd1.filterByRange("b", "d")//按照key在b,到d之間的範圍取出 
rdd2.colllect


flatMapValues
val a = sc.parallelize(List(("a", "1 2"), ("b", "3 4")))
rdd3.flatMapValues(_.split(" "))//把value進行flatmap,再和key結合 

foreach
這是一個action,但不會發生在driver,會在executor執行,在driver端是看不到的

foreachPartition
val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)
rdd1.foreachPartition(x => println(x.reduce(_ + _)))//一次拿出來一個分區,然後打印出每個分區中的數據進行聚合
#這說明任務執行的時候是在executor執行的 

 

RDD的產生和分配

 

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