spark中的transformation算子详解(需要进行shuffle的算子)(二)--- 图解+案例+源码分析

( transformation算子中的shuffle算子)

1、cogroup算子:(协分组、或者称为联合分组)

(1)group与cogroup:

①group:是将一个RDD里面的多个分区中的key相同的数据,通过shuffle进入到同一个分区的同一组中;

②cogroup:是将两个或多个RDD联合起来,通过shuffle将key相同的数据进入到同一个分区的同一个组中;得到的结果是一个RDD;

(2)cogroup的返回值:

①经过cogroup返回的新的RDD是一个对偶元组,key相同的value不是放入一个迭代器,而是两个或多个迭代器中;

②第一个迭代器:是第一个RDD中要分组的key的value;第二个迭代器:是第二个RDD中要分组的key的value;

③如果其中一个RDD中没有对应key的value值,则结果是一个空;或者打印出来是CompactBuffer()的样子;

(3)用cogroup做wordCount:(尽量不要使用)

简单实现如下:

import org.apache.spark.rdd.RDD
import org.apache.spark.{
   SparkConf, SparkContext}

//cogroup:协分组(或叫联合分组),很多算子底层都调用cogroup算子,会有shuffle产生,当处理大量数据时,会有大量shuffle,效率低。

object CoGroupDemo1 {
   
  def main(args: Array[String]): Unit = {
   
    val conf: SparkConf = new SparkConf().setAppName("CoGroupDemo").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
    //准备rdd
    val rdd1: RDD[(String, Int)] = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))
    val rdd2: RDD[(String, Int)] = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
    val rdd3: RDD[(String, Double)] = sc.parallelize(List(("tom", 1.0), ("shuke", 2.0)))
    //调用cogroup实现wordCount功能
    //注意:cogroup的几个RDD的类型必须保持一致
    val rdd4: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)
    val rdd5: RDD[(String, Int)] = rdd4.mapValues(tp => (tp._1.sum + tp._2.sum))
    //调用Action算子,并打印输出结果
    val result1: Array[(String, Int)] = rdd5.collect()
    println(result1.toBuffer)
  }
}

注:如果数据量大,会有大量的shuffle产生,尽量不要使用cogroup做wordCount。

(4)用union和reduceByKey代替cogroup做wordCount:

①最好不要用cogroup做wordCount,没有进行局部聚合,效率低;

②解决办法:如果多个RDD进行聚合做wordCount,可以先使用Union算子,然后调用reduceByKey;

Union算子:合并到一起,union后的分区数是几个RDD相加得到的分区数量;几个RDD的类型必须保持一致。

简单实现如下:

import org.apache.spark.rdd.RDD
import org.apache.spark.{
   SparkConf, SparkContext}

//cogroup:协分组(或叫联合分组),很多算子底层都调用cogroup算子,会有shuffle产生,当处理大量数据时,会有大量shuffle,效率低。

object CoGroupDemo2 {
   
  def main(args: Array[String]): Unit = {
   
    val conf: SparkConf = new SparkConf().setAppName("CoGroupDemo").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
    //准备rdd
    val rdd1: RDD[(String, Int)] = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))
    val rdd2: RDD[(String, Int)] = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
    val rdd3: RDD[(String, Double)] = sc.parallelize(List(("tom", 1.0), ("shuke", 2.0)))
	//调用union和reduceByKey做wordCount
    //注意:union的几个RDD的类型必须保持一致
	val rdd4: RDD[(String, Int)] = rdd1.union(rdd2)
	val rdd5: RDD[(String, Int)] = rdd4.reduceByKey(_ + _)
    //调用Action算子,并打印输出结果
	val result2: Array[(String, Int)] = rdd5.collect()
	println(result2.toBuffer)
  }
}

(5)cogroup对RDD的基本要求:

①cogroup不仅可以对两个RDD进行协分组,还可以对多个RDD进行协分组,cogroup中最多能传4个RDD,即:一个RDDcogroup四个RDD;如果有多于四个RDD进行cogroup,那就调用多次cogroup。

②cogroup可以自己cogroup自己(得到的两个迭代器是相同的)。

③cogroup要求的RDD类型必须是一致的,不一致会报错(union要求的RDD的类型也要是相同的)。

(6)cogroup与shuffle:

①cogroup中有几个stage?

cogroup有3个;

cogroup先进行了局部分组聚合,在全局聚合;

简单示意图如下:

注:图中写的几个stage不代表先后顺序;

​ stage的先后顺序看资源情况与调度分配,如果资源充足几个左面的两个stage同时进行,完成后右面的stage执行。

在这里插入图片描述

②什么是job?

Driver向Executor提交的作业;

触发一次Acition形成一个完整的DAG;

一个DAG对应一个Job;

一个Job中有一到多个Stage,一个Stage对应一个TaskSet,一个TaskSet中有一到多个Task。

③什么是DAG?

概念:有向无环图;

DAG是对多个RDD转换过程和依赖关系的描述;

触发Action就会形成一个完整的DAG,一个DAG就是一个Job;

④为什么有stage?

stage是任务执行阶段;

Stage执行是有先后顺序的,先执行前的,再执行后面的;如果不按顺序执行,结果会出错;

一个Stage对应一个TaskSet;一个TaskSet中的Task的数量取决于Stage中最后一个RDD分区的数量。

⑤什么是shuffle?

shuffle就是将上游数据按照一定规律(规律由分区器决定)将数据打散,分给下游的多个分区;严格地说,是下游的task到上游拉取数据;只要存在这种可能性,就是shuffle(跟数据有关)。

Shuffle的功能是将具相一定规律的数据按照指定的分区器的分区规则,通过网络,传输到指定地一台机器的一个分区中即Task中。

简单示意图如下:

在这里插入图片描述

(7)使用cogroup算子的简单案例:

import org.apache.spark.rdd.RDD
import org.apache.spark.{
   SparkConf, SparkContext}

//cogroup:协分组(或叫联合分组),很多算子底层都调用cogroup算子,会有shuffle产生,当处理大量数据时,会有大量shuffle,效率低。

object CoGroupDemo {
   
  def main(args: Array[String]): Unit = {
   
    val conf: SparkConf = new SparkConf().setAppName("CoGroupDemo").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
    //准备rdd
    val rdd1: RDD[(String, Int)] = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))
    val rdd2: RDD[(String, Int)] = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
    val rdd3: RDD[(String, Double)] = sc.parallelize(List(("tom", 1.0), ("shuke", 2.0)))

    //1、调用CoGroup算子,得到的是一个对偶元组,value中放的是两个或多个迭代器
    val rdd4: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)
    val result: Array[(String, (Iterable[Int], Iterable[Int]))] = rdd4.collect()
    println(result.toBuffer)
    /*
     cogroup与group的区别是:
     ①group:是将一个RDD里面的多个分区中的key相同的数据,通过shuffle进入到同一个分区的同一组中;
     ②cogroup:是将两个或多个RDD联合起来,通过shuffle将key相同的数据进入到同一个分区的同一个组中;得到的结果是一个RDD;
    */

/*
    //2、调用cogroup实现wordCount功能,不推荐,效率低
    val rdd4: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)
    val rdd5: RDD[(String, Int)] = rdd4.mapValues(tp => (tp._1.sum + tp._2.sum))
    //调用Action算子,并打印输出结果
    val result1: Array[(String, Int)] = rdd5.collect()
    println(result1.toBuffer)
*/

/*
    //3、调用union和reduceByKey实现wordCount功能
    //注意:union的几个RDD的类型必须保持一致
    val rdd4: RDD[(String, Int)] = rdd1.union(rdd2)
    val rdd5: RDD[(String, Int)] = rdd4.reduceByKey(_ + _)
    //调用Action算子,并打印输出结果
    val result2: Array[(String, Int)] = rdd5.collect()
    println(result2.toBuffer)
*/

/*
   4、cogroup多个RDD
    cogroup要求key的类型一定相同,但是value的类型可以不同
    一个rdd最多可以cogroup 3个RDD
 */
    val cogroup3: RDD[(String, (Iterable[Int], Iterable[Int], Iterable[Double]))] = rdd1.cogroup(rdd2, rdd3)
    //调用Action算子,并打印输出结果
    val result3: Array[(String, (Iterable[Int], Iterable[Int], Iterable[Double]))] = cogroup3.collect()
    println(result3.toBuffer)
  }
}

(8)cogroup的底层实现:

PairRDDFunction类中:
  /**
   * For each key k in `this` or `other1` or `other2` or `other3`,
   * return a resulting RDD that contains a tuple with the list of values
   * for that key in `this`, `other1`, `other2` and `other3`.
   */
  def cogroup[W1, W2, W3](other1: RDD[(K, W1)],
      other2: RDD[(K, W2)],
      other3: RDD[(K, W3)],
      partitioner: Partitioner)
      : RDD[(K, (Iterable[V], Iterable[W1], Iterable[W2], Iterable[W3]))] = self.withScope {
   
    if (partitioner.isInstanceOf[HashPartitioner] && keyClass.isArray) {
   
      throw new SparkException("HashPartitioner cannot partition array keys.")
    }
    val cg = new CoGroupedRDD[K](Seq(self, other1, other2, other3), partitioner)
    cg.mapValues {
    case Array(vs, w1s, w2s, w3s) =>
       (vs.asInstanceOf[Iterable[V]],
         w1s.asInstanceOf[Iterable[W1]],
         w2s.asInstanceOf[Iterable[W2]],
         w3s.asIns
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值