Transformation之有shuffle
#2 产生Shuffle的 在分布式计算中,将数据按照一定的计算逻辑(分区器),将具有相同规律的数据通过网络传输到指定的位置,严格是说是下游的task到上游拉取数据,上游一个分区的数据给了下游多个分区,只要存在这种可能性,就是shuffle(数据被打散)
reduceByKey(key相同聚合) groupByKey(根据key分组) groupBy(分组) distinct(过滤) aggregateByKey(key聚合) foldByKey(key聚合) partitionBy(指定分区) cogroup(协分组)join(连接) fullOuterJoin(全外连接) leftOuterJoin(左外连接) rightOuterJoin(右外连接) intersection(交集) subtract(差集) repartition(一定shuffle) coalsece(指定是否shuffle) sortby(排序) sortByKey(根据key排序)
combinByKeyWithClassTag
该方法要传入三个函数: 第一个函数为每个分区中key第一次出现对应value进行计算
第二个函数为在每个分区内,将key相同的value进行运算
第三个函数是将每个分区计算返回的结果, 是将分区编号相同的数据通过网络拉取过来,然后再将key相同的数据进行全局的运算
底层是new的shuffleRDD, 一个分区器, 可以set一个Aggregator(可以传入三个函数), 可以设置setMapSideCombine, 只有groupByKey和grouByKey为false
combinByKey 底层调用的是combineByKeyWithClass, mapSideCombine参数默认为true
reduceByKey
底层调用的是combineByKeyWithClassTag, 并在方法中new的shuffleRDD, mapSideCombine为true,局部聚合的逻辑和全局聚合的逻辑一样,可以先局部聚合,在全局聚合, 优点是可以减少shuffle数据的网络传输,提高效率
特殊情况:调用reduceByKey不一定进行shuffle,如果前面的数据已经分好区了, 那就不需要在进行分区了(使用同样的分区器(列如都是Hashpartitioner),并且分区数量相同)
package cn.doit.spark.day02.demo02
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/*
reduceByKey 有shuffle
*/
object ReduceByKeyDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("ReduceByKeyDemo").setMaster("local[*]")
val sc: SparkContext = new SparkContext(conf)
val lines: RDD[String] = sc.textFile(args(0))
val wordAndOne: RDD[(String, Int)] = lines.flatMap(_.split(" ")).map((_, 1))
// val reduced: RDD[(String, Int)] = wordAndOne.reduceByKey(_ + _)
//第一个函数:该分区内第一次出现key对应的value进行运算
val f1 = (x:Int) => x
//第二个函数:在该分区内将key相同的value继续进行局部聚合
val f2 = (x:Int , y:Int) => x + y
//第三个函数全局聚合
val f3 = (a:Int , b:Int) => a + b
//reduceByKey底层调用的就是combineByKey,再底层就是调用的ShuffledRDD
//底层调用的是combineByKeyWithClassTag, 并在方法中中new的ShuffleRDD
val reduced = wordAndOne.combineByKey(f1, f2, f3)
//仅分区不排序,可以选择性的要不要排序
//val shuffledRDD: ShuffledRDD[String, Int, Int] = new ShuffledRDD[String, Int, Int]
//(wordAndOne, new HashPartitioner(wordAndOne.partitions.length))
// true是局部聚合,false是不局部聚合
//shuffledRDD.setMapSideCombine(true)
//聚合器,将三个参数聚合
//val aggregator = new Aggregator[String, Int, Int](f1, f2, f3)
//shuffledRDD.setAggregator(aggregator)
//val res = shuffledRDD.collect()
//println(res.toBuffer)
//将数据放进一个数组
val res = reduced.collect()
//打印
println(res.toBuffer)
}
}
groupByKey(分组)
底层调用的是combinByKeyWithClassTag, mapSideCombine参数默认为true
底层也是先局部再全局 效率高于groupBy 推荐使用
package cn.doit.spark.day02.demo03
import org.apache.spark.rdd.{PairRDDFunctions, RDD, ShuffledRDD}
import org.apache.spark.{Aggregator, HashPartitioner, SparkConf, SparkContext}
import scala.collection.mutable.ArrayBuffer
object GroupByKeyDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("GroupByKeyDemo").setMaster("local[*]")
val sc: SparkContext = new SparkContext(conf)
val lines: RDD[String] = sc.textFile(args(0))
val wordAndOne: RDD[(String, Int)] = lines.flatMap(_.split(" ")).map((_, 1))
// val grouped = wordAndOne.groupByKey()
// val grouped = new PairRDDFunctions[String, Int](wordAndOne).groupByKey()
//第一个参数, 把第一个key对应的value装进可变数组中
val f1 = (a:Int) => ArrayBuffer[Int](a)
//第二个参数,局部分组,将相同key分到一组,对应的value放进ArrayBuffer中
val f2 = (b:ArrayBuffer[Int], c:Int) => b += c
//第三个参数,全局分组,将每个分区的结果分组,key相同的分到一组 ,将数组合并到一起
val f3 = (x:ArrayBuffer[Int], y:ArrayBuffer[Int]) => x ++= y
val shuffledRdd: ShuffledRDD[String, Int, ArrayBuffer[Int]] = new ShuffledRDD[String, Int, ArrayBuffer[Int]](wordAndOne, new HashPartitioner(wordAndOne.partitions.length))
shuffledRdd.setMapSideCombine(false)
//聚合器
shuffledRdd.setAggregator(new Aggregator[String, Int, ArrayBuffer[Int]](f1,f2,f3))
val grouped = shuffledRdd.collect()
println(grouped.toBuffer)
// val tuples = grouped.collect()
// println(tuples.toBuffer)
}
}
groupBy(分组)
底层调用的是combinByKeyWithClassTag, mapSideCombine参数默认为false
比grou