• 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到磁盘的操作.


  • 相关阅读:
    LeetCode_4——寻找两个有序数组的中位数
    Java的CAS与ABA问题
    跨域问题解决
    解决git-for-windows官网下载速度慢的问题
    Java对观察者模式的支持
    Java动态代理
    设计模式七大原则
    UML中的类图关系
    布隆过滤器(Bloom Filter)与Hash算法
    Ubuntu16安装fabric1.4.4环境
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8441067.html
Copyright © 2020-2023  润新知