文章目录
- 一、Value 类型
- 1.1 map(func)
- 1.2 mapPartition(func)
- 1.3 mapPartitionWithIndex(func)
- 1.4 flatMap(func)
- 1.5 glom
- 1.6 groupBy(func)
- 1.7 filter(func)
- 1.8 sample(withReplacement, fraction, seed)
- 1.9 distinct([numTasks])
- 1.10 coalesce(numPartitions, shuffle = false)
- 1.11 rapartition(numPartitions)
- 1.12 sortBy(func, [ascending], [numTasks])
- 二、双 Value 类型交互
- 三、Key-Value 类型
- 四、案例
- 五、Action 算子
一、Value 类型
1.1 map(func)
-
作用:返回一个新的 RDD,该 RDD 由每一个输入元素经过 func 函数转换后组成
-
示例:创建一个 1- 10 数组的 RDD,将所有元素 * 2 形成新的 RDD
scala> var source = sc.parallelize(1 to 10) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[8] at parallelize at <console>:24 scala> val mapadd = source.map(_ * 2) mapadd: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[9] at map at <console>:26 scala> mapadd.collect() res8: Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
1.2 mapPartition(func)
-
作用:类似于 map,但独立地在 RDD 的每个分区上运行,因此在类型为 T 的 RDD 上运行时,func 的函数类型必须是 Iterator[T] => Iterator[U](即对每个分区中的数据进行操作),假设由 N 个元素,有 M 个分区,那么 map 算子将被调用 N 次(计算将被从 Driver 发送到 Executor N 次),而 mapPartitions 算子被调用 M 次(计算被发送 M 次),如果在map函数中有创建对象的操作,那会导致每行数据都创建一个对象;所以 mapPartitions 的效率更高,但是所需的内存更大,可能导致内存溢出,因为mapPartition需要把遍历迭代器时产生的中间结果都保存在一个集合中,然后等遍历结束后再返回;
-
示例:创建一个RDD,使每个元素*2组成新的RDD
scala> val source = sc.makeRDD(1 to 10, 2) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[16] at makeRDD at <console>:24 //这里的 map 是 scala 中针对于集合的 map 操作,而不是 rdd 的 map 算子 scala> val mapPartitionRDD = source.mapPartitions(x => x.map(_ * 2)) mapPartitionRDD: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[17] at mapPartitions at <console>:25 scala> mapPartitionRDD.collect res21: Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
1.3 mapPartitionWithIndex(func)
-
作用:类似于 mapPartitions,但是可以在不同分区上执行不同的操作 ,func的函数类型必须是(Int, Interator[T]) => Iterator[U];
-
示例:创建一个RDD,使每个元素跟所在分区形成一个元组组成一个新的RDD
scala> val source = sc.makeRDD(1 to 10, 3) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[18] at makeRDD at <console>:24 scala> val mapPartitionsWithIndexRDD = source.mapPartitionsWithIndex((part, data) => data.map((part, _))) mapPartitionsWithIndexRDD: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[19] at mapPartitionsWithIndex at <console>:25 scala> mapPartitionsWithIndexRDD.collect res22: Array[(Int, Int)] = Array((0,1), (0,2), (0,3), (1,4), (1,5), (1,6), (2,7), (2,8), (2,9), (2,10))
1.4 flatMap(func)
-
作用:类似于map,但是每一个输入元素可以被映射为0或多个输出元素(所以func应该返回一个序列,而不是单一元素)
-
示例:
val source = sc.makeRDD(1 to 10) val flatMapRDD = source.flatMap(multi) flatMapRDD.collect().foreach(print) def multi(x: Int): Array[Int] = { val array: Array[Int] = new Array[Int](x + 1) for(i <- 1 to x) { array(i) = i * x } array } // 0 1 // 0 2 4 // 0 3 6 9 // 0 4 8 16 // ...
1.5 glom
- 作用:将每一个分区形成要给数组,形成新的 RDD 类型是 RDD[Array[T]]
- 示例:创建一个4个分区的RDD,并将每个分区的数据放到一个数组
1.6 groupBy(func)
-
作用:分组,按照传入函数的返回值进行分组,把相同的 key 对应的值放入一个迭代器。
-
示例:创建一个RDD,按照元素模以2的值进行分组。
scala> val source = sc.parallelize(1 to 20) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24 scala> val groupByRDD = source.groupBy(_ % 3) groupByRDD: org.apache.spark.rdd.RDD[(Int, Iterable[Int])] = ShuffledRDD[2] at groupBy at <console>:25 scala> groupByRDD.collect res0: Array[(Int, Iterable[Int])] = Array((0,CompactBuffer(12, 15, 18, 3, 6, 9)), (2,CompactBuffer(11, 14, 17, 20, 2, 5, 8)), (1,CompactBuffer(1, 4, 7, 10, 13, 16, 19)))
1.7 filter(func)
-
作用:过滤。返回一个新的 RDD,该 RDD 由经过 func 函数计算后返回值为 true 的输入元素组成
-
示例:创建一个 RDD,过滤出一个新的 RDD(元素大于 5 的)
scala> val source = sc.parallelize(List(1,2, 4, 24, 245, 3, 4)) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[3] at parallelize at <console>:24 scala> val filterRDD = source.filter(_ > 5) filterRDD: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[4] at filter at <console>:25 scala> filterRDD.collect res2: Array[Int] = Array(24, 245)
1.8 sample(withReplacement, fraction, seed)
-
作用:对数组进行采样,withReplacement 指的有无放回,如果又放回,则 fraction 代表每个数据希望被选择多少次(泊松算法);如果无放回,faction 指的是每条数据被采集到的可能性,大于该值的数据才被采集;seed 代表随机数种子。
-
创建一个RDD(1-10),从中选择放回和不放回抽样
scala> val source = sc.parallelize(1 to 10) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[5] at parallelize at <console>:24 scala> val sample1 = source.sample(true, 3, 10) sample1: org.apache.spark.rdd.RDD[Int] = PartitionwiseSampledRDD[6] at sample at <console>:25 scala> sample1.collect res3: Array[Int] = Array(1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 9, 9, 10, 10, 10) scala> val sample2 = source.sample(false, 0.4, 10) sample2: org.apache.spark.rdd.RDD[Int] = PartitionwiseSampledRDD[7] at sample at <console>:25 scala> sample2.collect res4: Array[Int] = Array(1, 2, 3, 4, 5, 9)
1.9 distinct([numTasks])
-
作用:对源 RDD 进行去重后返回一个新的 RDD。默认情况下,只有 8 个并行任务来操作,但是可以传入一个可选的 numTasks 参数来改变它
-
注意:distinct 会把一个分区中的数据打乱重组,所以产生了 shuffle 过程,在 spark 的转换操作中,没有 shuffle 的操作更快
-
需求:创建一个 RDD,对其进行去重
scala> val source = sc.parallelize(Array(1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 9, 9, 10, 10, 10)) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[8] at parallelize at <console>:24 scala> val distinct = source.distinct distinct: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[11] at distinct at <console>:25 scala> distinct.collect res5: Array[Int] = Array(4, 6, 8, 10, 2, 1, 7, 3, 9, 5)
1.10 coalesce(numPartitions, shuffle = false)
-
作用:改变分区数
-
注意:如果把分区数从大的变小,就把 shuffle 设置成 false,因为不需要打乱一个分区内的数据;如果把分区数从小的变大,就把 shuffle 设置成 true,可以用于解决某个分区中的数据量过大的问题。
-
需求:创建一个 4 个分区的 RDD,改变其分区
scala> val source = sc.parallelize(Array(1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 9, 9, 10, 10, 10), 4) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[12] at parallelize at <console>:24 //缩减分区 scala> val coalesce1 = source.coalesce(2, false) coalesce1: org.apache.spark.rdd.RDD[Int] = CoalescedRDD[16] at coalesce at <console>:25 scala> coalesce1.glom.collect res6: Array[Array[Int]] = Array(Array(1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6), Array(6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 9, 9, 10, 10, 10)) //扩大分区 scala> val coalesce2 = source.coalesce(8, true) coalesce2: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[24] at coalesce at <console>:25 scala> coalesce2.glom.collect res8: Array[Array[Int]] = Array(Array(2, 6, 5, 7), Array(6, 8, 2, 6), Array(4, 9, 3, 7), Array(3, 7, 4, 9), Array(4, 10, 4, 7), Array(7, 5, 10), Array(1, 5, 10), Array(5, 7, 1, 6))
1.11 rapartition(numPartitions)
- 作用:改变分区数
- 注意;repartition 的底层实现是 coalesce,并且把 shuffle 设置成了 true,所以在加大分区数的时候用 repartition,缩小分区数的时候用 coalesce 来避免 shuffle
1.12 sortBy(func, [ascending], [numTasks])
-
作用:使用 func 先对数据进行处理,按照处理后的数据比较结果排序,默认为正序
-
示例:创建一个 RDD,按照不同的规则进行排序
scala> val source = sc.parallelize(List(1, 3 ,4 ,12, 34, 4, 134)) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[26] at parallelize at <console>:24 //按照原数据的大小升序排序 scala> val sort1 = source.sortBy(x => x) sort1: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[31] at sortBy at <console>:25 scala> sort1.collect res9: Array[Int] = Array(1, 3, 4, 4, 12, 34, 134) //按照原数据与 3 取模的大小降序排序 scala> val sort2 = source.sortBy(x => x%3, false) sort2: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[36] at sortBy at <console>:25 scala> sort2.collect res10: Array[Int] = Array(134, 1, 4, 34, 4, 3, 12)
二、双 Value 类型交互
2.1 union(otherRDD)
-
作用:对源 RDD 和参数 RDD 求并集后返回一个新的 RDD
-
示例:创建两个 RDD,求并集
//source1 有 4 个分区 scala> val source1 = sc.parallelize(1 to 5, 4) source1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[38] at parallelize at <console>:24 //source2 有 3 个分区 scala> val source2 = sc.parallelize(3 to 8, 3) source2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[39] at parallelize at <console>:24 scala> val unionRDD = source1.union(source2) unionRDD: org.apache.spark.rdd.RDD[Int] = UnionRDD[40] at union at <console>:27 //合并结果有 7 个分区,所以是每个父 RDD 的分区都对应一个子 RDD 的分区 scala> unionRDD.glom.collect res9: Array[Array[Int]] = Array(Array(1), Array(2), Array(3), Array(4, 5), Array(3, 4), Array(5, 6), Array(7, 8))
2.2 subtract(otherRDD)
-
作用:对两个 RDD 求差集
-
注意:会有 shuffle
-
示例:创建两个 RDD,求第一个 RDD 与第二个 RDD 的差集
scala> val source1 = sc.parallelize(1 to 5) source1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[40] at parallelize at <console>:24 scala> val source2 = sc.parallelize(3 to 8) source2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[41] at parallelize at <console>:24 scala> val subtractRDD = source1.subtract(source2) subtractRDD: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[49] at subtract at <console>:27 scala> subtractRDD.collect res12: Array[Int] = Array(2, 1) //顺序不是原先的顺序了,因为被 shuffle 过了
2.3 intersection(otherRDD)
-
作用:对两个 RDD 求交集
-
注意:会有 shuffle
-
示例:计算两个 RDD 的交集
scala> val source1 = sc.parallelize(1 to 5) source1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24 scala> val source2 = sc.parallelize(3 to 8) source2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[1] at parallelize at <console>:24 scala> val intersectionRDD = source1.intersection(source2) intersectionRDD: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[7] at intersection at <console>:27 scala> intersectionRDD.collect res0: Array[Int] = Array(4, 3, 5) 顺序不是原先的顺序了,因为被 shuffle 过了
2.4 zip(otherRDD)
-
作用:将两个 RDD 组合成 key/value 形式的 RDD,两个 RDD 的分区数量以及元素个数必须相同,否则会抛异常。(没有 shuffle)
-
示例:创建两个RDD,并将两个RDD组合到一起形成一个(k,v)RDD
//创建两个 RDD scala> val source1 = sc.parallelize('a' to 'e', 4) source1: org.apache.spark.rdd.RDD[Char] = ParallelCollectionRDD[24] at parallelize at <console>:24 scala> val source2 = sc.parallelize(1 to 5, 4) source2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[23] at parallelize at <console>:24 //做 zip 操作 scala> val zipRDD = source1.zip(source2) zipRDD: org.apache.spark.rdd.RDD[(Char, Int)] = ZippedPartitionsRDD2[25] at zip at <console>:27 //查看每个分区中的数据 scala> source1.glom.collect res10: Array[Array[Char]] = Array(Array(a), Array(b), Array(c), Array(d, e)) scala> source2.glom.collect res11: Array[Array[Int]] = Array(Array(1), Array(2), Array(3), Array(4, 5)) //可以发现,zip 之后的分区中的数据与之前还是一一对应的 scala> zipRDD.glom.collect res12: Array[Array[(Char, Int)]] = Array(Array((a,1)), Array((b,2)), Array((c,3)), Array((d,4), (e,5)))
三、Key-Value 类型
3.1 partitionBy(partitioner)
-
作用:对 pairRDD 使用指定的分区器进行分区操作,如果原有的分区器和指定的分区器或者分区个数是一致的话就不进行分区, 否则会生成ShuffleRDD,即会产生shuffle过程。
-
示例:创建一个4个分区的RDD,对其重新分区
//创建数据源 scala> val source = sc.makeRDD(Array((1, "aaa"), (2, "bbb"), (3, "ccc"), (4, "ddd")), 4) source: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[0] at makeRDD at <console>:24 //查看原分区 scala> source.glom.collect res0: Array[Array[(Int, String)]] = Array(Array((1,aaa)), Array((2,bbb)), Array((3,ccc)), Array((4,ddd))) //使用 hashPartitioner 把四个分区改成两个 scala> val partitionByRDD = source.partitionBy(new org.apache.spark.HashPartitioner(2)) partitionByRDD: org.apache.spark.rdd.RDD[(Int, String)] = ShuffledRDD[2] at partitionBy at <console>:25 //这一步产生了 shuffle 过程(在命令行里运行有 shuffle 闪过) scala> partitionByRDD.glom.collect res2: Array[Array[(Int, String)]] = Array(Array((2,bbb), (4,ddd)), Array((1,aaa), (3,ccc))) //使用 hashPartitioner 把把四个分区改成四个 scala> val partitionByRDD2 = source.partitionBy(new org.apache.spark.HashPartitioner(4)) partitionByRDD2: org.apache.spark.rdd.RDD[(Int, String)] = ShuffledRDD[4] at partitionBy at <console>:25 //这一步没有产生 shuffle 过程(在命令行里运行没有 shuffle 闪过) scala> partitionByRDD2.glom.collect res3: Array[Array[(Int, String)]] = Array(Array((4,ddd)), Array((1,aaa)), Array((2,bbb)), Array((3,ccc)))
3.2 groupByKey()
-
作用:把相同 key 对应的值聚合到一个 sequence 中
-
注意:复习这里的时候看看源码,这些 ByKey 的算子底层实现都是同一个函数
-
示例:创建一个pairRDD,将相同key对应值聚合到一个sequence中,并计算相同key对应值的相加结果。
//创建数据源 scala> val source = sc.makeRDD(Array(("a", 1), ("a", 1), ("a", 3), ("b", 1), ("b", 1), ("c", 1), ("d", 1)), 4) source: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[12] at makeRDD at <console>:24 //groupByKey scala> val groupByKeyRDD = source.groupByKey() groupByKeyRDD: org.apache.spark.rdd.RDD[(String, Iterable[Int])] = ShuffledRDD[13] at groupByKey at <console>:25 //查看结果 scala> groupByKeyRDD.collect res9: Array[(String, Iterable[Int])] = Array((d,CompactBuffer(1)), (a,CompactBuffer(1, 1, 3)), (b,CompactBuffer(1, 1)), (c,CompactBuffer(1))) //计算 wordCount scala> val wordCount = groupByKeyRDD.map(x => (x._1, x._2.sum)) wordCount: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[14] at map at <console>:25 scala> wordCount.collect res10: Array[(String, Int)] = Array((d,1), (a,5), (b,2), (c,1))
3.3 reduceByKey(func, [numTasks])
-
作用:在一个 (K, V) 的 RDD 上调用,返回一个 (K, V) 的 RDD,使用指定的 reduce 函数,将相同 key 的值聚合到一起,reduce 任务的个数可以通过第二个可选的参数来设定。
-
示例:创建一个pairRDD,计算相同key对应值的相加结果
scala> val source = sc.makeRDD(Array(("a", 1), ("a", 1), ("a", 3), ("b", 1), ("b", 1), ("c", 1), ("d", 1)), 4) source: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[15] at makeRDD at <console>:24 scala> val reduceByKeyRDD = source.reduceByKey((x, y) => (x + y)) reduceByKeyRDD: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[16] at reduceByKey at <console>:25 scala> reduceByKeyRDD.collect res12: Array[(String, Int)] = Array((d,1), (a,5), (b,2), (c,1))
-
reduceByKey 与 groupByKey 的区别:
- reduceByKey 按照 key 进行聚合,在 shuffle 之前有 combine(预聚合)操作,返回结果是 RDD(K, V)
- groupByKey 按照 key 进行分组,直接进行 shuffle
3.4 aggregateByKey(zeroValue: U, partitioner: Partitioner)(seqOp: (U, V) => U, combOp: (U, U) => U)
-
作用:先将分区内的数据按照 seqOp 进行合并,再将分区内合并的结果按照 combOp 进行合并,zeroValue 代表在进行分区内合并时每一个 key 对应的初始值。
-
示例:
- wordCount(思路就是分区内和分区间用相同的函数即可)
scala> val aggregateByKeyRDD = source.aggregateByKey(0)(_ + _, _ + _) aggregateByKeyRDD: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[18] at aggregateByKey at <console>:25 scala> aggregateByKeyRDD.collect res13: Array[(String, Int)] = Array((d,1), (a,5), (b,2), (c,1))
- 创建一个 pairRDD,并取出每个分区内相同 key 的最大值,然后相加
scala> val source = sc.makeRDD(Array(("a", 1), ("a", 1), ("a", 3), ("b", 1), ("b", 1), ("c", 1), ("d", 1)), 3) source: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[2] at makeRDD at <console>:24 // 查看未聚合时的分区 scala> source.glom.collect res1: Array[Array[(String, Int)]] = Array(Array((a,1), (a,1)), Array((a,3), (b,1)), Array((b,1), (c,1), (d,1))) scala> val aggregateByKeyRDD = source.aggregateByKey(0)(math.max(_,_), _ + _) aggregateByKeyRDD: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[4] at aggregateByKey at <console>:25 scala> aggregateByKeyRDD.collect res2: Array[(String, Int)] = Array((c,1), (d,1), (a,4), (b,2))
3.5 foldByKey
3.6 combineByKey
-
作用:对相同的 key,把 V 合并成一个集合
-
参数说明:
-
createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就和之前的某个元素的键相同。如果这是一个新的元素,combineByKey()会使用一个叫作createCombiner()的函数来创建那个键对应的累加器的初始值
-
mergeValue: 如果这是一个在处理当前分区之前已经遇到的键,它会使用mergeValue()方法将该键的累加器对应的当前值与这个新的值进行合并
-
mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器, 就需要使用用户提供的 mergeCombiners() 方法将各个分区的结果进行合并。
-
-
示例: 创建一个pairRDD,根据key计算每种key的均值。(先计算每个key出现的次数以及可以对应值的总和,再相除得到结果)
3.7 sortByKey
-
作用:在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD
-
注意:有 shuffle
-
示例:创建一个pairRDD,按照key的正序进行排序
scala> val source = sc.makeRDD(Array((1, "aa"), (2, "bb"), (3, "cc"), (4, "dd")), 4) source: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[6] at makeRDD at <console>:24 scala> source.glom.collect res4: Array[Array[(Int, String)]] = Array(Array((1,aa)), Array((2,bb)), Array((3,cc)), Array((4,dd))) scala> val sortByKeyRDD = source.sortByKey(true) sortByKeyRDD: org.apache.spark.rdd.RDD[(Int, String)] = ShuffledRDD[10] at sortByKey at <console>:25 scala> sortByKeyRDD.collect res5: Array[(Int, String)] = Array((1,aa), (2,bb), (3,cc), (4,dd))
3.8 mapValues
-
作用:针对于 (K, V) 形式的类型只对 V 进行操作
-
示例:创建一个pairRDD,并将value添加字符串"|||"
scala> val source = sc.makeRDD(Array((1, "aa"), (2, "bb"), (3, "cc"), (4, "dd")), 4) source: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[16] at makeRDD at <console>:24 scala> val mapValuesRDD = source.mapValues(_ + "|||") mapValuesRDD: org.apache.spark.rdd.RDD[(Int, String)] = MapPartitionsRDD[17] at mapValues at <console>:25 scala> mapValuesRDD.collect res8: Array[(Int, String)] = Array((1,aa|||), (2,bb|||), (3,cc|||), (4,dd|||))
3.9 join
-
作用:在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD
-
示例:创建两个pairRDD,并将key相同的数据聚合到一个元组。
scala> val source1 = sc.makeRDD(Array((1, "aa"), (2, "bb"), (3, "cc"), (4, "dd")), 4) source1: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[33] at makeRDD at <console>:24 scala> val source2 = sc.makeRDD(Array((1, 1), (2, 2), (3, 3), (4, 4)), 3) source2: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[34] at makeRDD at <console>:24 scala> val joinRDD = source1.join(source2) joinRDD: org.apache.spark.rdd.RDD[(Int, (String, Int))] = MapPartitionsRDD[37] at join at <console>:27 scala> joinRDD.glom.collect res13: Array[Array[(Int, (String, Int))]] = Array(Array((4,(dd,4))), Array((1,(aa,1))), Array((2,(bb,2))), Array((3,(cc,3))))
3.10 cogroup
-
作用:在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable,Iterable))类型的RDD
-
示例:创建两个pairRDD,并将key相同的数据聚合到一个迭代器。
scala> val source1 = sc.makeRDD(Array((1, "aa"), (1, "AA"), (2, "bb"), (3, "cc"), (4, "dd")), 4) source1: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[52] at makeRDD at <console>:24 scala> val source2 = sc.makeRDD(Array((1, 1),(1, 5), (1, 5), (2, 2), (3, 3), (4, 4)), 3) source2: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[49] at makeRDD at <console>:24 scala> val cogroupRDD = source1.cogroup(source2) cogroupRDD: org.apache.spark.rdd.RDD[(Int, (Iterable[String], Iterable[Int]))] = MapPartitionsRDD[54] at cogroup at <console>:27 scala> cogroupRDD.collect res17: Array[(Int, (Iterable[String], Iterable[Int]))] = Array((4,(CompactBuffer(dd),CompactBuffer(4))), (1,(CompactBuffer(AA, aa),CompactBuffer(1, 5, 5))), (2,(CompactBuffer(bb),CompactBuffer(2))), (3,(CompactBuffer(cc),CompactBuffer(3))))
四、案例
-
数据结构:时间戳,省份,城市,用户,广告,中间字段使用空格分割。
-
需求:统计出每一个省份广告被点击次数的TOP3
import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} object Top3 { def main(args: Array[String]): Unit = { val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("top3") val sc: SparkContext = new SparkContext(conf) //数据结构:时间戳,省份,城市,用户,广告,中间字段使用空格分割。 // 需求:统计出每一个省份广告被点击次数的TOP3 val data: RDD[String] = sc.textFile("spark-review/input/agent.log") //((p, ad), 1) val province_ad_one: RDD[((String, String), Int)] = data.map(line => { val infos = line.split(" ") ((infos(1), infos(4)), 1) }) //((p, ad), count) val province_ad_count: RDD[((String, String), Int)] = province_ad_one.reduceByKey(_ + _) //(p, (add, count)) val province_add: RDD[(String, (String, Int))] = province_ad_count.map(data => { (data._1._1, (data._1._2, data._2)) }) //按照 key 聚合 val province_group: RDD[(String, Iterable[(String, Int)])] = province_add.groupByKey() //把每个 key 中对应的列表排序并去前 3 位 val unit: RDD[(String, List[(String, Int)])] = province_group.mapValues(ads => { val list = ads.toList list.sortWith((x, y) => x._2 > y._2).take(3) }) unit.collect.foreach(println) } }
五、Action 算子
5.1 reduce()
-
作用:通过 func 函数聚集 RDD 中的所有元素,先聚合分区内的数据,再聚合分区间的数据
-
示例:创建一个RDD,将所有元素聚合得到结果。
scala> val rdd1 = sc.makeRDD(1 to 10, 2) rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[55] at makeRDD at <console>:24 scala> rdd1.reduce(_ + _) res18: Int = 55
5.2 collect()
-
作用:在驱动程序中,以数组的形式返回数据集的所有元素。
-
示例:创建一个RDD,并将RDD内容收集到Driver端打印
scala> val rdd1 = sc.makeRDD(1 to 10, 2) rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[55] at makeRDD at <console>:24 scala> rdd1.reduce(_ + _) res18: Int = 55 scala> val source = sc.makeRDD(1 to 10) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[56] at makeRDD at <console>:24 //这里的 foreach 是 scala 集合的foreach scala> source.collect.foreach(x => print(x + " ")) 1 2 3 4 5 6 7 8 9 10
5.3 foreach()
-
作用:对 RDD 中的所有元素采用同样的操作
-
注意:该算子的具体实行步骤是在 Executor 端执行的,而不是 Driver 端,所以如果使用 standlone 启动 spark-shell 的话,控制台不会有任何输出,如果启动 local 模式,只用一个线程输出结果是有序的,用多个线程输出结果就是无须的了
-
示例:打印 RDD 中的所有元素
scala> val source = sc.makeRDD(1 to 10, 3) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[2] at makeRDD at <console>:24 scala> source.foreach(x => print(x + " ")) //结果无序 1 2 3 7 8 4 5 6 9 10
5.3 count()
-
作用:返回 RDD 中的元素的个数
-
示例:创建一个 RDD,统计该 RDD 的条数
scala> val source = sc.makeRDD(1 to 10, 3) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[3] at makeRDD at <console>:24 scala> source.count res4: Long = 10
5.4 first()
-
作用:返回 RDD 中的第一个元素
-
示例:创建一个 RDD,返回该 RDD 中第一个元素
scala> val source = sc.makeRDD(1 to 10, 3) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[4] at makeRDD at <console>:24 scala> source.first() res5: Int = 1
5.5 take(n)
-
作用:返回一个由 RDD 的前 n 个元素组成的数组
-
示例:创建一个 RDD,返回前 3 个元素
scala> val source = sc.makeRDD(1 to 10, 3) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[5] at makeRDD at <console>:24 scala> source.take(3) res6: Array[Int] = Array(1, 2, 3)
5.5 takeOrdered(n)
-
作用:返回该RDD排序后的前n个元素组成的数组
-
示例:创建一个 RDD,排序后返回前三个元素
scala> val source = sc.makeRDD(Array(2,5,4,6,8,3)) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[6] at makeRDD at <console>:24 scala> source.takeOrdered(3) res10: Array[Int] = Array(2, 3, 4)
5.6 aggregate(zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U)
-
作用:将每个分区里面的元素通过 seqOp 和初始值进行聚合,然后用 combine 函数对每个分区的结果和初始值进行 combine 操作
-
示例:
scala> val source = sc.makeRDD(1 to 10, 2) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[11] at makeRDD at <console>:24 scala> source.glom.collect res16: Array[Array[Int]] = Array(Array(1, 2, 3, 4, 5), Array(6, 7, 8, 9, 10)) scala> source.aggregate(0)(math.max(_, _), _ + _) 15
5.7 fold:简化过的 aggregate
-
作用:简化过的 aggregate 操作,分区内和分区间的函数一样
-
示例:
scala> val source = sc.makeRDD(1 to 10, 2) source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[14] at makeRDD at <console>:24 scala> source.fold(0)(_ + _) res20: Int = 55