spark中的aggregateByKey算子详解

spark中的aggregateByKey算子详解:

源码解析:

源码有三种格式:

/**
    * 自定义分区器Partitioner
    * @param zeroValue 初始值(默认值)
    * @param partitioner 自定义分区器
    * @param seqOp
    * @param combOp
    * @tparam U
    * @return
    */
  def aggregateByKey[U: ClassTag](zeroValue: U, partitioner: Partitioner)(seqOp: (U, V) => U,
                                                                          combOp: (U, U) => U): RDD[(K, U)] = self.withScope {
    // Serialize the zero value to a byte array so that we can get a new clone of it on each key
    val zeroBuffer = SparkEnv.get.serializer.newInstance().serialize(zeroValue)
    val zeroArray = new Array[Byte](zeroBuffer.limit)
    zeroBuffer.get(zeroArray)

    lazy val cachedSerializer = SparkEnv.get.serializer.newInstance()
    val createZero = () => cachedSerializer.deserialize[U](ByteBuffer.wrap(zeroArray))

    // We will clean the combiner closure later in `combineByKey`
    val cleanedSeqOp = self.context.clean(seqOp)
    combineByKeyWithClassTag[U]((v: V) => cleanedSeqOp(createZero(), v),
      cleanedSeqOp, combOp, partitioner)
  }

 /**
    * 默认使用hash分区器 HashPartitioner
    * @param zeroValue 初始值(默认值)
    * @param numPartitions 分区个数
    * @param seqOp
    * @param combOp
    * @tparam U
    * @return
    */
  def aggregateByKey[U: ClassTag](zeroValue: U, numPartitions: Int)(seqOp: (U, V) => U,
                                                                    combOp: (U, U) => U): RDD[(K, U)] = self.withScope {
    aggregateByKey(zeroValue, new HashPartitioner(numPartitions))(seqOp, combOp)
  }

 /**
    * 默认使用RDD的分区
    * @param zeroValue 初始值
    * @param seqOp 
    * @param combOp
    * @tparam U
    * @return
    */
  def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U,
                                                combOp: (U, U) => U): RDD[(K, U)] = self.withScope {
    aggregateByKey(zeroValue, defaultPartitioner(self))(seqOp, combOp)
  }

参数定义:

以上述第三个aggregateByKey为例, 三个参数的含义分别为:
第一个参数是, 每个key的初始值 ,比如为0,nil,null;
第二个是个函数, Seq Function, 经测试这个函数就是用来先对 每个分区 内的数据 按照key分别进行 函数定义的操作;
第三个是个函数, Combiner Function, 对经过 Seq Function 处理过的数据 按照key分别进行 函数定义的操作。

scala代码测试:

 def main(args: Array[String]): Unit = {
    Logger.getLogger("org").setLevel(Level.OFF)
    val spark = SparkSession.builder().master("local").getOrCreate()
    val list =  List((1,2),(2,7),(1,3),(2,8),(3,9),(3,10)
      ,(1,4),(1,5),(2,6),(2,11),(3,12),(3,13))

    val listRDD = spark.sparkContext.parallelize(list,2)

    println(listRDD.getNumPartitions)
    listRDD.aggregateByKey(0)((a,b)=>{
      println("第一个:"+a+","+b)
      math.max(a,b)
    },(x,y)=>{
      println("第二个:"+x+","+y)
      x+y
    }).collect().foreach(println)
  }

代码详解:

listRDD分为两个分区:
第一个分区:(1,2),(2,7),(1,3),(2,8),(3,9),(3,10)
第二个分区:(1,4),(1,5),(2,6),(2,11),(3,12),(3,13)

打印结果:
第一个:0,2   
第一个:0,7
第一个:2,3
第一个:7,8
第一个:0,9
第一个:9,10
第一个:0,4
第一个:4,5
第一个:0,6
第一个:6,11
第一个:0,12
第一个:12,13

第二个:3,5
第二个:8,11
第二个:10,13

(1,8)
(2,19)
(3,23)

首先第一个参数计算:

  1. 遍历第一个值为(1,2)的时候,a是key为1的累加器,会把初始值zeroValue赋给这个累加器,b代表键值对(1,2)中的值2,之后将返回值2赋回给这个累加器;
  2. 遍历第二个值为(2,7)的时候,a是key为2的累加器,会把初始值zeroValue赋给这个累加器,b代表键值对(2,7)中的值7,之后将返回值7赋回给这个累加器;
  3. 遍历第二个值为(1,3)的时候,a是key为1的累加器,由于之前第一步骤中已经有key为1的累加器,并且值为2,b代表键值对(1,3)中的值3,之后将返回值3赋回给这个累加器;
  4. 遍历第二个值为(2,8)的时候,a是key为2的累加器,由于之前第2步骤中已经有key为2的累加器,并且值为7,b代表键值对(2,8)中的值8,之后将返回值8赋回给这个累加器;
  5. 以此类推,计算出第一分区的数据;
  6. 同理计算出第二分区的数据;

第一个参数最后的返回值为:

第一分区:
(1,3)
(2,8)
(3,10)
第二分区:
(1,5)
(2,11)
(3,13)

其次第二个参数计算:
第二个参数是计算所有分区相同key的数据集(x,y):

key=1时,(3,5)
key=2时,(8,11)
key=3时:(10,13)

最后计算结果:

(1,8)
(2,19)
(3,23)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值