一、spark rdd 介紹
1.rdd概念
rdd是彈性分佈式數據集,存儲在硬盤或者內存上。
2.rdd特徵
1)有一個分片列表,就是能被切分,和Hadoop一樣,能夠切分的數據才能夠並行計算
2)由一個函數計算每一個分片
3)對其他rdd有依賴,但並不是所有的rdd都有依賴
4)key-value的rdd是根據哈希來分區的
二、spark rdd創建
1.數據集合
val data = Array(1, 2,3, 4, 5, 6, 7, 8, 9)
val distData = sc.parallelize(data, 3)
2.外部數據源
val distFile1 = sc.textFile("data.txt") //本地當前目錄下文件
val distFile2=sc.textFile("hdfs://192.168.1.100:9000/input/data.txt") //HDFS文件
val distFile3 =sc.textFile("file:/input/data.txt") //本地指定目錄下文件
val distFile4 =sc.textFile("/input/data.txt") //本地指定目錄下文件
textFile("/input/001.txt, /input/002.txt ") //讀取多個文件
textFile("/input") //讀取目錄
textFile("/input /*.txt") //含通配符的路徑
textFile("/input /*.gz") //讀取壓縮文件
三、rdd算子分類及功能
1.rdd算子作用:
1)輸入:在Spark程序運行中,數據從外部數據空間(如分佈式存儲:textFile讀取HDFS等,parallelize方法輸入Scala集合或數據)輸入Spark,數據進入Spark運行時數據空間,轉化爲Spark中的數據塊,通過BlockManager進行管理。
2)運行:在Spark數據輸入形成RDD後便可以通過變換算子,如fliter等,對數據進行操作並將RDD轉化爲新的RDD,通過Action算子,觸發Spark提交作業。如果數據需要複用,可以通過Cache算子,將數據緩存到內存。
3)輸出:程序運行結束數據會輸出Spark運行時空間,存儲到分佈式存儲中(如saveAsTextFile輸出到HDFS),或Scala數據或集合中(collect輸出到Scala集合,count返回Scala int型數據)。
2.rdd算子分類:
1)Value數據類型的Transformation算子,這種變換並不觸發提交作業,針對處理的數據項是Value型的數據。
2)Key-Value數據類型的Transfromation算子,這種變換並不觸發提交作業,針對處理的數據項是Key-Value型的數據對。
3)Action算子,這類算子會觸發SparkContext提交Job作業。
四、spark rdd轉換操作
1.map
map是對RDD中的每個元素都執行一個指定的函數來產生一個新的RDD;RDD之間的元素是一對一關係;
val rdd1 = sc.parallelize(1 to 9, 3)
val rdd2 = rdd1.map(x => x*2)
rdd2.collect
res3: Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18)
2.filter
Filter是對RDD元素進行過濾;返回一個新的數據集,是經過func函數後返回值爲true的原元素組成;
val rdd3 = rdd2. filter (x => x> 10)
rdd3.collect
res4: Array[Int] = Array(12, 14, 16, 18)
3.flatMap
flatMap類似於map,但是每一個輸入元素,會被映射爲0到多個輸出元素(因此,func函數的返回值是一個Seq,而不是單一元素),RDD之間的元素是一對多關係;
val rdd4 = rdd3. flatMap (x => x to 20)
res5: Array[Int] = Array(12, 13, 14, 15, 16, 17, 18, 19, 20, 14, 15, 16, 17, 18, 19, 20, 16, 17, 18, 19, 20, 18, 19, 20)
4.mapPartitions
mapPartitions是map的一個變種。map的輸入函數是應用於RDD中每個元素,而mapPartitions的輸入函數是每個分區的數據,也就是把每個分區中的內容作爲整體來處理的。
5.mapPartitionsWithIndex
mapPartitionsWithSplit與mapPartitions的功能類似, 只是多傳入split index而已,所有func 函數必需是 (Int, Iterator<T>) =>Iterator<U> 類型。
6.sample
sample(withReplacement,fraction,seed)是根據給定的隨機種子seed,隨機抽樣出數量爲frac的數據。withReplacement:是否放回樣;fraction:比例,0.1表示10% ;
val a = sc.parallelize(1 to 10000, 3)
a.sample(false, 0.1, 0).count
res24: Long = 960
7.union
union(otherDataset)是數據合併,返回一個新的數據集,由原數據集和otherDataset聯合而成。
val rdd8 = rdd1.union(rdd3)
rdd8.collect
res14: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 16, 18)
8.intersection
intersection(otherDataset)是數據交集,返回一個新的數據集,包含兩個數據集的交集數據;
val rdd9 = rdd8.intersection(rdd1)
rdd9.collect
res16: Array[Int] = Array(6, 1, 7, 8, 2, 3, 9, 4, 5)
9.distinct
distinct([numTasks]))是數據去重,返回一個數據集,是對兩個數據集去除重複數據,numTasks參數是設置任務並行數量。
val rdd10 = rdd8.union(rdd9).distinct
rdd10.collect
res19: Array[Int] = Array(12, 1, 14, 2, 3, 4, 16, 5, 6, 18, 7, 8, 9)
10.groupByKey
groupByKey([numTasks])是數據分組操作,在一個由(K,V)對組成的數據集上調用,返回一個(K,Seq[V])對的數據集。
val rdd0 = sc.parallelize(Array((1,1), (1,2) , (1,3) , (2,1) , (2,2) , (2,3)), 3)
val rdd11 = rdd0.groupByKey()
rdd11.collect
res33: Array[(Int, Iterable[Int])] = Array((1,ArrayBuffer(1, 2, 3)), (2,ArrayBuffer(1, 2, 3)))
11.reduceByKey
reduceByKey(func, [numTasks])是數據分組聚合操作,在一個(K,V)對的數據集上使用,返回一個(K,V)對的數據集,key相同的值,都被使用指定的reduce函數聚合到一起。
val rdd12 = rdd0.reduceByKey((x,y) => x + y)
rdd12.collect
res34: Array[(Int, Int)] = Array((1,6), (2,6))
12.aggregateByKey
aggreateByKey(zeroValue: U)(seqOp: (U, T)=> U, combOp: (U, U) =>U) 和reduceByKey的不同在於,reduceByKey輸入輸出都是(K,
V),而aggreateByKey輸出是(K,U),可以不同於輸入(K, V) ,aggreateByKey的三個參數:
zeroValue: U,初始值,比如空列表{} ;
seqOp: (U,T)=> U,seq操作符,描述如何將T合併入U,比如如何將item合併到列表 ;
combOp: (U,U) =>U,comb操作符,描述如果合併兩個U,比如合併兩個列表 ;
所以aggreateByKey可以看成更高抽象的,更靈活的reduce或group 。
val z = sc.parallelize(List(1,2,3,4,5,6), 2)
z.aggreate(0)(math.max(_, _), _ + _)
res40: Int = 9
val z = sc.parallelize(List((1, 3), (1, 2), (1, 4), (2, 3)))
z.aggregateByKey(0)(math.max(_, _), _ + _)
res2: Array[(Int, Int)] = Array((2,3), (1,9))
13.combineByKey
combineByKey是對RDD中的數據集按照Key進行聚合操作。聚合操作的邏輯是通過自定義函數提供給combineByKey。
combineByKey[C](createCombiner: (V) ⇒ C, mergeValue: (C, V) ⇒ C, mergeCombiners: (C, C)
⇒ C, numPartitions: Int):RDD[(K, C)]把(K,V) 類型的RDD轉換爲(K,C)類型的RDD,C和V可以不一樣。combineByKey三個參數:
val data = Array((1, 1.0), (1, 2.0), (1, 3.0), (2, 4.0), (2, 5.0), (2, 6.0))
val rdd = sc.parallelize(data, 2)
val combine1 = rdd.combineByKey(createCombiner = (v:Double) => (v:Double, 1),
mergeValue = (c:(Double, Int), v:Double) => (c._1 + v, c._2 + 1),
mergeCombiners = (c1:(Double, Int), c2:(Double, Int)) => (c1._1 + c2._1, c1._2 + c2._2),numPartitions = 2 )combine1.collect
res0: Array[(Int, (Double, Int))] = Array((2,(15.0,3)), (1,(6.0,3)))
14.sortByKey
sortByKey([ascending],[numTasks])是排序操作,對(K,V)類型的數據按照K進行排序,其中K需要實現Ordered方法。
val rdd14 = rdd0.sortByKey()
rdd14.collect
res36: Array[(Int, Int)] = Array((1,1), (1,2), (1,3), (2,1), (2,2), (2,3))
15.join
join(otherDataset, [numTasks])是連接操作,將輸入數據集(K,V)和另外一個數據集(K,W)進行Join, 得到(K, (V,W));該操作是對於相同K的V和W集合進行笛卡爾積 操作,也即V和W的所有組合;
val rdd15 = rdd0.join(rdd0)
rdd15.collect
res37: Array[(Int, (Int, Int))] = Array((1,(1,1)), (1,(1,2)), (1,(1,3)), (1,(2,1)), (1,(2,2)), (1,(2,3)), (1,(3,1)), (1,(3,2)), (1,(3,3)), (2,(1,1)),(2,(1,2)), (2,(1,3)), (2,(2,1)), (2,(2,2)), (2,(2,3)), (2,(3,1)), (2,(3,2)), (2,(3,3)))
連接操作除join 外,還有左連接、右連接、全連接操作函數: leftOuterJoin、rightOuterJoin、fullOuterJoin。
16.cogroup
cogroup(otherDataset, [numTasks])是將輸入數據集(K, V)和另外一個數據集(K, W)進行cogroup,得到一個格式爲(K, Seq[V], Seq[W])的數據集。
val rdd16 = rdd0.cogroup(rdd0)
rdd16.collect
res38: Array[(Int, (Iterable[Int], Iterable[Int]))] = Array((1,(ArrayBuffer(1, 2, 3),ArrayBuffer(1, 2, 3))), (2,(ArrayBuffer(1, 2,3),ArrayBuffer(1, 2, 3))))
17.cartesian
cartesian(otherDataset)是做笛卡爾積:對於數據集T和U 進行笛卡爾積操作, 得到(T, U)格式的數據集。
val rdd17 = rdd1.cartesian(rdd3)
rdd17.collect
res39: Array[(Int, Int)] = Array((1,12), (2,12), (3,12), (1,14), (1,16), (1,18), (2,14), (2,16), (2,18), (3,14), (3,16), (3,18), (4,12), (5,12),(6,12), (4,14), (4,16), (4,18), (5,14), (5,16), (5,18), (6,14), (6,16), (6,18), (7,12), (8,12), (9,12), (7,14), (7,16), (7,18), (8,14), (8,16),(8,18), (9,14), (9,16), (9,18))
五、spark rdd行動操作
1.reduce
reduce(func)是對數據集的所有元素執行聚集(func)函數,該函數必須是可交換的。
val rdd1 = sc.parallelize(1 to 9, 3)
val rdd2 = rdd1.reduce(_ + _)
rdd2: Int = 45
2.collect
collect相當於toArray,toArray已經過時不推薦使用,collect將分佈式的RDD返回爲一個單機的scala Array數組。在這個數組上運用scala的函數式操作。
rdd1.collect()
res8: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
3.count
返回數據集中元素的個數。
rdd1.count()
res9: Long = 9
4.first
返回數據集中的第一個元素, 類似於take(1)。
rdd1.first()
res10: Int = 1
5.take
Take(n)返回一個包含數據集中前n個元素的數組, 當前該操作不能並行。
rdd1.take(3)
res11: Array[Int] = Array(1, 2, 3)
6.takeSample
takeSample(withReplacement,num, [seed])返回包含隨機的num個元素的數組,和Sample不同,takeSample 是行動操作,所以返回的是數組而不是RDD , 其中第一個參數withReplacement是抽樣時是否放回,第二個參數num會精確指定抽樣數,而不是比例。
rdd1.takeSample(true, 4)
res15: Array[Int] = Array(9, 5, 5, 6)
7.takeOrdered
takeOrdered(n, [ordering])是返回包含隨機的n個元素的數組,按照順序輸出。
rdd1.takeOrdered(4)
res16: Array[Int] = Array(1, 2, 3, 4)
8.saveAsTextFile
把數據集中的元素寫到一個文本文件,Spark會對每個元素調用toString方法來把每個元素存成文本文件的一行。存儲到HDFS的指定目錄。
9.countByKey
對於(K, V)類型的RDD. 返回一個(K, Int)的map, Int爲K的個數。
10.foreach
foreach(func)是對數據集中的每個元素都執行func函數。不返回RDD和Array,而是返回Uint。
11.saveAsObjectFile
saveAsObjectFile將分區中的每10個元素組成一個Array,然後將這個Array序列化,映射爲(Null,BytesWritable(Y))的元素,寫入HDFS爲SequenceFile的格式。
12.collectAsMap
collectAsMap對(K,V)型的RDD數據返回一個單機HashMap。對於重複K的RDD元素,後面的元素覆蓋前面的元素。
13.reduceByKeyLocally
實現的是先reduce再collectAsMap的功能,先對RDD的整體進行reduce操作,然後再收集所有結果返回爲一個HashMap。
14.lookup
Lookup函數對(Key,Value)型的RDD操作,返回指定Key對應的元素形成的Seq。這個函數處理優化的部分在於,如果這個RDD包含分區器,則只會對應處理K所在的分區,然後返回由(K,V)形成的Seq。如果RDD不包含分區器,則需要對全RDD元素進行暴力掃描處理,搜索指定K對應的元素。
15.top
top可返回最大的k個元素。
16.reduce
reduce函數相當於對RDD中的元素進行reduceLeft函數的操作。函數實現如下。
Some(iter.reduceLeft(cleanF))
reduceLeft先對兩個元素<K,V>進行reduce函數操作,然後將結果和迭代器取出的下一個元素<k,V>進行reduce函數操作,直到迭代器遍歷完所有元素,得到最後結果。
在RDD中,先對每個分區中的所有元素<K,V>的集合分別進行reduceLeft。每個分區形成的結果相當於一個元素<K,V>,再對這個結果集合進行reduceleft操作。
17.fold
fold和reduce的原理相同,但是與reduce不同,相當於每個reduce時,迭代器取的第一個元素是zeroValue。