Spark RDD操作講解

一、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。
發佈了58 篇原創文章 · 獲贊 40 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章