spark transform系列__groupByKey

这个操作的作用根据相同的key的所有的value存储到一个集合中的一个玩意.

def groupByKey(): RDD[(KIterable[V])] = self.withScope {
  groupByKey(defaultPartitioner(self))
}

在 做groupByKey的操作时,由于需要根据key对数据进行重新的分区操作,因此这个操作需要有一个partitioner的实例.默认是hash算 子.这个操作根据当前操作的RDD中是否有partitioner,同时这个partitioner与当前的传入的partitioner的实例是否相同 来判断是否需要执行shuffle操作.

如果是默认的hashPartitioner时,检查spark.default.parallelism配置是否有配置,如果有分区个数按这个配置来设置,否则使用当前进行此groupByKey操作的rdd的partitions来设置.

 

def groupByKey(partitioner: Partitioner): RDD[(KIterable[V])]

 = self.withScope {

这里同样与reduceByKey的操作一样,通过调用combineByKeyWithClassTag的函数来进行处理,

不同的是,withClassTag的合并操作是一个CompactBuffer[V]类型.

这里生成aggregator实例需要的三个函数时,

createCombiner:如果key目前还有值时,根据当前传入的key-value中的value生成一个CompactBuffer的实例,并存储到key对应的位置,

mergeValue:传入一个key-value时,如果key对应的CompactBuffer已经存在,把这个value添加到这个buffer中.

mergeCombiners:这个主要在shuffle结束时,把key相同的多个buffer进行合并.

需要注意的是,在执行groupByKey的操作时,会把mapSideCombine设置为false,表示不执行map端的聚合.

为 什么groupByKey不做mapSideCombine的操作呢,因为在groupByKey的操作中,会先根据相同的key,把value存储到一 个buffer中,这个地方并不会设计到map端combine的操作会减少多少的网络传输的开效,如果做map combine操作时,反而增加了map端writer的内存使用.

  // groupByKey shouldn't use map side combine because map side combine does not
  // reduce the amount of data shuffled and requires all map side data be inserted
  // into a hash table, leading to more objects in the old gen.
  val createCombiner = (v: V) => CompactBuffer(v)
  val mergeValue = (buf: CompactBuffer[V]v: V) => buf += v
  val mergeCombiners = (c1: CompactBuffer[V]c2: CompactBuffer[V]) => c1 ++= c2
  val bufs = combineByKeyWithClassTag[CompactBuffer[V]](
    createCombinermergeValuemergeCombinerspartitionermapSideCombine = false)
  bufs.asInstanceOf[RDD[(KIterable[V])]]
}

 

在combineByKeyWithClassTag的操作函数中的处理:

mapSideCombine的传入参数为false.

这个地方,根据上面的三个函数,生成Aggregator,这里的K,V,C分别代表key的类型,value的类型,C在groupByKey的操作中是一个CompactBuffer[V]的类型

val aggregator = new Aggregator[KVC](
  self.context.clean(createCombiner),
  self.context.clean(mergeValue),
  self.context.clean(mergeCombiners))

这里主要是看看当前的partitioner是否与当前执行这个操作的rdd的partitioner实例相同.相同就不在需要执行shuffle操作,否则就需要执行shuffle操作,生成新的ShuffledRDD实例.
if (self.partitioner == Some(partitioner)) {
  self.mapPartitions(iter => {
    val context = TaskContext.get()
    new InterruptibleIterator(contextaggregator.combineValuesByKey(iter,

        context))
  }preservesPartitioning = true)
else {
  new ShuffledRDD[KVC](selfpartitioner)
    .setSerializer(serializer)
    .setAggregator(aggregator)
    .setMapSideCombine(mapSideCombine)
}

 

在Aggregator的操作中,如果mapSideCombine的参数为false时,通过Aggregator中的combineValuesByKey函数执行数据的合并操作.如果mapSideCombine的参数为true时,通过Aggregator中的combineCombinersByKey函数执行数据的合并操作(只执行第三个函数,因为map端已经把结果合并成了C的类型).

在Aggregator的合并操作中,通过ExternalAppendOnlyMap实例来进行数据的合并(insertAll).这个实例会最大可能的使用内存,如果内存实在不够用时,考虑对内存中的数据进行spill到磁盘的操作.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值