spark transform系列__cogroup

本文详细介绍了 Spark 的 Cogroup 函数,它用于将两个 RDD 中相同 key 的元素进行合并,返回的 RDD 的 value 是一个 Pair,包含了两个 Iterable 的值。在执行 Cogroup 操作时,可能需要进行 shuffle,具体取决于输入 RDD 是否已经 shuffle 过以及 partitioner 是否相同。文中还深入解析了 CoGroupedRDD 的关键代码,包括 getDependencies、compute 函数以及 ExternalAppendOnlyMap 在合并过程中的作用。

Cogroup

cogroup的函数实现:

这个实现根据两个要进行合并的两个RDD操作,生成一个CoGroupedRDD的实例,这个RDD的返回结果是把相同的key中两个RDD分别进行合并操作,最后返回的RDD的value是一个Pair的实例,这个实例包含两个Iterable的值,第一个值表示的是RDD1中相同KEY的值,第二个值表示的是RDD2中相同key的值.

由于做cogroup的操作,需要通过partitioner进行重新分区的操作,因此,执行这个流程时,需要执行一次shuffle的操作(如果要进行合并的两个RDD的都已经是shuffle后的rdd,同时他们对应的partitioner相同时,就不需要执行shuffle,)

def cogroup[W](other: RDD[(KW)]partitioner: Partitioner)
    : RDD[(K(Iterable[V]Iterable[W]))] = self.withScope {
  if (partitioner.isInstanceOf[HashPartitioner] && keyClass.isArray) {
    throw new SparkException("Default partitioner cannot partition array keys.")
  }

生成CoGroupedRDD的实例,并根据这个实例执行mapValues的操作.这个的结果是一个Pair的实例,

Pair._1是左RDD对应key的结果集,Pair._2是右RDD对应key的结果集.
  val cg = new CoGroupedRDD[K](Seq(selfother)partitioner)
  cg.mapValues { case Array(vsw1s) =>
    (vs.asInstanceOf[Iterable[V]]w1s.asInstanceOf[Iterable[W]])
  }
}

 

这里说明下关于GoGroupedRDD的一些关键代码:

重写的getDependencies函数:

rdds.map { rdd: RDD[_] =>
  if (rdd.partitioner == Some(part)) {
    logDebug("Adding one-to-one dependency with " + rdd)
    new OneToOneDependency(rdd)
  } else {

针对这个CoGroupedRDD的实例上层依赖的两个RDD中,如果上层的RDD不是一个SHUFFLE的RDD,或者说上层的RDD的SHUFFLE算子与当前的RDD的算子不相同时,

     这个RDD在执行时,还需要进行SHUFFLE的操作.

在生成这个依赖时使用到的CoGroupCombiner类型=>

 

private type CoGroup = CompactBuffer[Any]
private type CoGroupValue = (Any, Int)  // Int is dependency number
private type CoGroupCombiner = Array[CoGroup]


    logDebug("Adding shuffle dependency with " + rdd)
    new ShuffleDependency[KAnyCoGroupCombiner](
      rdd.asInstanceOf[RDD[_ <: Product2[K_]]]partserializer)
  }
}

 

针对CoGroupedRDD的compute函数的关键部分代码:

这个函数中,首先根据CoGroupedRDD的上层的依赖的RDD,生成一个rddIterators的数组.每个RDD的结果的Iterator都存储到数组对应的index中.

val rddIterators = new ArrayBuffer[(Iterator[Product2[KAny]], Int)]

 

这里根据RDD对上层的依赖的RDD进行迭代,得到上层RDD中对应的部分的数据集的Iterator,并存储到对应的数组位置中.
for ((depdepNum) <- dependencies.zipWithIndex) dep match {
  case oneToOneDependency: OneToOneDependency[Product2[KAny]] @unchecked =>

上层依赖的RDD是一个OneToOneDependency的RDD时,表示这个RDD的SHUFFLE算子与上层的RDD的SHUFFLE的算子相同,不需要进行SHUFFLE的操作,直接得到上层RDD的iterator的结果集,存储到对应的数组index中.
    val dependencyPartition = split.narrowDeps(depNum).get.split
    // Read them from the parent
    val it = oneToOneDependency.rdd.iterator(dependencyPartitioncontext)
    rddIterators += ((itdepNum))

  case shuffleDependency: ShuffleDependency[___] =>

这种情况下,表示在执行这个RDD前,后一个SHUFFLE的执行,对上层依赖的RDD进行了重新分区的操作,通过shuffleManager的reader,得到上层RDD中重新对应此分区数据的数据集.存储到对应的数组index中.
    // Read map outputs of shuffle
    val it = SparkEnv.get.shuffleManager
      .getReader(shuffleDependency.shuffleHandlesplit.indexsplit.index 1context)
      .read()
    rddIterators += ((itdepNum))
}

 

接下来的代码中首先生成一个ExternalAppendOnlyMap[KCoGroupValueCoGroupCombiner]实例,在这个生成的实例中,K表示这个RDD的KEY的类型,

CoGroupValue类型为CoGroupValue = (Any, Int).这里的Any其实是上层RDD的VLAUE的类型.

CoGroupCombiner类型为Array[CoGroup].这个数组的长度为2.对应上层的两个RDD.

CoGroup类型为CompactBuffer[Any].这里存储的Any是对应具体的上层RDD的值.

                                           也就是key相同的value的集合体.

val map = createExternalMap(numRdds)
for ((itdepNum) <- rddIterators) {

迭代每个RDD的返回的iterator的结果,每次迭代时,把上层rdd的value包装成CoGroupValue的实例.

把每个RDD中的结果通过ExternalAppendOnlyMap把相同的key进行合并操作.这个操作与shuffle中的groupByKey与reduceByKey的操作中使用到的三个函数的流程完全相同,
  map.insertAll(it.map(pair => (pair._1new CoGroupValue(pair._2depNum))))
}

 

接下来看看在CoGroupedRDD中数据compute的计算时,ExternalAppendOnlyMap合并结果集时的三个操作函数的定义:

 

createCombiner函数,这个函数在key第一次被发现时,用于生成CoGroupCombiner的实例,并把这个value添加到这个实例数组中对应的位置上(根据上层RDD对应的位置)

val createCombiner: (CoGroupValue => CoGroupCombiner) = value => {
  val newCombiner = Array.fill(numRdds)(new CoGroup)
  newCombiner(value._2) += value._1
  newCombiner
}

 

mergeValue函数,这个函数把key相同的value合并对对应此上层RDD的位置的Buffer的集合中.这个主要是针对一个相同的partition地结果的合并.

val mergeValue: (CoGroupCombinerCoGroupValue) => CoGroupCombiner =
  (combinervalue) => {
  combiner(value._2) += value._1
  combiner
}

 

mergeCombiners函数,把两个buffer进行合并,最终生成一个buffer,主要用于多partition合并.

val mergeCombiners: (CoGroupCombinerCoGroupCombiner) => CoGroupCombiner =
  (combiner1combiner2) => {
    var depNum = 0
    while (depNum < numRdds) {
      combiner1(depNum) ++= combiner2(depNum)
      depNum += 1
    }
    combiner1
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值