一、aggregateByKey
學過hadoop的話其實就很好理解aggregateByKey了。
def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U,combOp: (U, U) => U): RDD[(K, U)] = self.withScope
首先,這個函數是處於數據類型爲key-value形式的時候纔有的。
然後該函數是柯里化函數,有兩個括號的參數:
1、第一個括號是:zeroValue,表示初始值,第二個括號的第一個參數(分區內reduce)會用到,而第二個參數(分區間reduce)不會用到初始值
2、第二個括號:
(1)第一個參數:相當於hadoop mapreduce中mapper中的combiner。不同分區(相當於mapreduce的不同map)中的每個分區的數據都各自按key分組,然後key相同的數據列表使用該第一個參數進行執行scala的fold操作。
(2)第二個參數:相當於hadoop mapreduce中的reducer。不同的分區經過第一個參數的之後,每個分區的數據合併爲一個新的列表。。再使用第二個參數做reduceByKey操作。。
下面是我自己根據aggregateByKey原理封裝的代碼與原版aggregateByKey的對比:
def main(args: Array[String]): Unit = {
val sc = new SparkContext(new SparkConf().setMaster("local[*]").setAppName("WC"))
val sourceDataRdd = sc.parallelize(List("a" -> 1, "a" -> 2, "b" -> 3, "z" -> 4, "f" -> 6))
//初始值(aggregateByKey的第一個括號的參數)
val zeroValue = 10
//aggregateByKey第二個括號的第一個參數
val firstFunc = (left: Int, right: Int) => left + right
//aggregateByKey第二個括號的第二個參數
val secondFunc = (left: Int, right: Int) => left + right
//使用aggregateByKey
val result1 = sourceDataRdd
.aggregateByKey(zeroValue)(firstFunc, secondFunc)
.collect()
.mkString(",")
//根據原理自己封裝的aggregateByKey
val result2 = sourceDataRdd
.mapPartitions(
_.toList.groupBy(_._1) //根據key分組
.mapValues(_.map(_._2)) // values由List[(String,Int)] 轉爲 List[Int]
.mapValues(_.fold(zeroValue)(firstFunc)) //使用初始值進行fold
.toIterator
)
.reduceByKey(secondFunc)
.collect()
.mkString(",")
println(result1)
println(result2)
sc.stop()
}
執行輸出結果如下:(分區數和我不一樣的話輸出順序也不一樣。)
(f,16),(a,23),(z,14),(b,13)
(f,16),(a,23),(z,14),(b,13)
二、aggregate
參數與aggregateByKey一樣。。
1、第一個括號是初始值。
2、第二個括號
(1)第一個參數:對分區內的所有數據(一個list)進行fold操作(初始值就是第一個括號的參數),最終一個分區的結果只會有一個值。
(2)第二個參數:N個分區經過第一個參數之後得到N個值(一個新的list),然後根據這N個值再次進行fold操作(初始值再次被用上),初始值在分區內與分區間都會用上,這是與aggregateByKey的一個不同點,aggregateByKey在分區間時不會使用初始值。
執行輸出結果如下:
def main(args: Array[String]): Unit = {
val sc = new SparkContext(new SparkConf().setMaster("local[*]").setAppName("WC"))
val sourceDataRdd = sc.parallelize(List(1, 2, 3, 5, 67, 123))
//初始值(aggregateByKey的第一個括號的參數)
val zeroValue = 10
//aggregateByKey第二個括號的第一個參數
val firstFunc = (left: Int, right: Int) => left + right
//aggregateByKey第二個括號的第二個參數
val secondFunc = (left: Int, right: Int) => left + right
//使用aggregateByKey
val result1 = sourceDataRdd
.aggregate(zeroValue)(firstFunc, secondFunc)
//根據原理自己封裝的aggregateByKey
val result2 = sourceDataRdd
.mapPartitions(iterator => List(iterator.fold(zeroValue)(firstFunc)).toIterator)
.collect() //轉爲scala的集合再使用fold而不是使用rdd本身的fold(因爲誤會)
.fold(zeroValue)(secondFunc)
println(result1)
println(result2)
sc.stop()
}
輸出結果:、
271
271