spark transform系列__intersection

这个transform返回的是进行操作的两个RDD中,key-value都相同的所有的数据集的新的RDD.说白了就是把两个RDD中数据完全相同的数据进行保留,不相同的数据直接丢弃掉.这个操作会执行shuffle操作.

 

实现代码:

 

def intersection(other: RDD[T]): RDD[T] = withScope {

这里首先执行对第一个rdd的map操作,执行完成map操作后,新生成的map rdd的key就是原来rdd的kv,value就一个null,

其次执行对第二个rdd的map操作,执行完成map操作后,新生成的map rdd的key就是原来rdd的kv,value就一个null,

第三步:把两个执行完成map操作后的新的两个rdd,这两个rdd的key都变成了原来的kv,value都是null,把这两个新的rdd执行cogroup的操作,这个操作把两个rdd中相同的key的数据进行合并,value就一个数组,数组的长度是2(两个rdd),数组中第0个下标有值,表示这个key在第0个rdd中存在,第1个下标有值,表示这个key在第1个下标有值,如果两个都有值,表示这个key(原来老的rdd的key-value)在两个rdd中都存在.
  this.map(v => (vnull)).cogroup(other.map(v => (vnull)))

第4步,执行filter操作,这个操作过滤掉key对应的value的数组中,只包含一个值的所有的数据,也就是说这个filter完成后,返回的rdd中的数据是合并操作的两个rdd中key与value都相同的所有的数据的新的RDD的数据集.
      .filter { case (_(leftGrouprightGroup)) => 

             leftGroup.nonEmpty && rightGroup.nonEmpty }

最后一个操作,这里返回合并后,两个RDD都包含的key-value的key的部分的值,这个值,其实就是原始的rdd的key-value.
      .keys
}

 

实现代码的分析:

首先,根据当前的rdd做map操作,这个作用是把原来的kv当作k,value为null.

然后,把传入的要进行处理的rdd做同样的操作.

 

接下来,开始做cogroup操作,通过这两个RDD,

def cogroup[W](other: RDD[(KW)]): RDD[(K(Iterable[V]Iterable[W]))] = self.withScope {
  cogroup(otherdefaultPartitioner(selfother))
}

 

来看看defaultPartitioner的实现:

这里根据两个RDD中,根据RDD中的PARTITION的个数,倒排序,并进行迭代,如果传入的RDD中某一个RDD包含有partitioner的算子时,直接使用这个RDD的paritioner算子.

def defaultPartitioner(rdd: RDD[_]others: RDD[_]*): Partitioner = {
  val bySize = (Seq(rdd) ++ others).sortBy(_.partitions.size).reverse
  for (r <- bySize if r.partitioner.isDefined 

           && r.partitioner.get.numPartitions > 0) {
    return r.partitioner.get
  }

流程执行到这里,表示传入的两个RDD,都没有partitioner,这个时候,如果下面代码中的配置项存在,直接使用这个配置的默认的并行度来生成HashPartitioner,否则根据RDD的partition个数最多的partitions进行生成HashPartitioner实例.
  if (rdd.context.conf.contains("spark.default.parallelism")) {
    new HashPartitioner(rdd.context.defaultParallelism)
  } else {
    new HashPartitioner(bySize.head.partitions.size)
  }
}

 

根据生成好的Partitioner实例,再次调用cogroup的重载.

def cogroup[W](other: RDD[(KW)]partitioner: Partitioner)
    : RDD[(K(Iterable[V]Iterable[W]))] = self.withScope {

这里进行下检查,如果是Hash分区的算子时,key不能是array,array不支持hashcode.
  if (partitioner.isInstanceOf[HashPartitioner] && keyClass.isArray) {
    throw new SparkException("Default partitioner cannot partition array keys.")
  }

 

这里生成一个CoGroupedRDD的实例.
  val cg = new CoGroupedRDD[K](Seq(selfother)partitioner)


  cg.mapValues { case Array(vsw1s) =>
    (vs.asInstanceOf[Iterable[V]]w1s.asInstanceOf[Iterable[W]])
  }
}

 

下面来看看CoGroupedRDD的定义的关键部分:

这个部分包含实例的生成,上层RDD依赖,PARTITION,与compute这几个部分:

实例生成:在这个实例生成时,默认没有指定上层RDD的依赖.

class CoGroupedRDD[K: ClassTag](
    @transient var rdds: Seq[RDD[_ <: Product2[K_]]],
    part: Partitioner)
  extends RDD[(KArray[Iterable[_]])](rdds.head.context, Nil) 

由于在实例生成时没有指定RDD的依赖,因此,这个RDD的依赖通常是一个特殊的依赖

接下来看看getDependencies函数.

在这个函数中,如果上层的RDD中包含有partitioner时,那么针对这个rdd的依赖是一个OneToOneDependency的依赖,如果上层的RDD中包含的partitioner与当前新生成的RDD的partitioner是同一个实例时,那么针对这个rdd的依赖是一个OneToOneDependency的依赖,

如果上层的两个RDD都不包含partitioner时,两个RDD的上层依赖都是ShuffleDependency.

如果生成的上层RDD的依赖是ShuffleDependency时,针对这个RDD需要单独生成stage执行.

override def getDependencies: Seq[Dependency[_]] = {
  rdds.map { rdd: RDD[_] =>
    if (rdd.partitioner == Some(part)) {
      logDebug("Adding one-to-one dependency with " + rdd)
      new OneToOneDependency(rdd)
    } else {
      logDebug("Adding shuffle dependency with " + rdd)
      new ShuffleDependency[KAnyCoGroupCombiner](
        rdd.asInstanceOf[RDD[_ <: Product2[K_]]]partserializer)
    }
  }
}

 

 

下面再看看CoGroupedRDD中getPartitions函数:

针对一个cogroup的rdd,这个rdd的partition的个数由Partitioner对应的partition的个数得到.

override def getPartitions: Array[Partition] = {
  val array = new Array[Partition](part.numPartitions)
  for (i <- until array.length) {

迭代生成当前的RDD对应的每一个的partition.

在生成这个Partition时,实例的第一个参数是对应的partition的下标,

第二个参数是一个数组,这个数组是当前的partition对应的上层的RDD的依赖的RDD,与RDD对应的Partition.如果上层依赖是一个ShuffleDependency时,数组对应此RDD的下标位置是一个None.

也就是说Partition第二个参数对应的上层依赖RDD的下标位置如果不是一个None时,那么针对这个上层的RDD就是一个OneToOneDependency的依赖.
    // Each CoGroupPartition will have a dependency per contributing RDD
    array(i) = new CoGroupPartition(irdds.zipWithIndex.map { case (rddj) =>
      // Assume each RDD contributed a single dependency, and get it
      dependencies(j) match {
        case s: ShuffleDependency[___] =>
          None
        case _ =>
          Some(new NarrowCoGroupSplitDep(rddirdd.partitions(i)))
      }
    }.toArray)
  }
  array
}

 

最后,看看针对CoGroupedRDD的compute的实现逻辑:

 

override def compute(s: Partitioncontext: TaskContext)

Iterator[(KArray[Iterable[_]])] = {

得到要计算的partition,并得到这个RDD依赖的上层RDD的个数.
  val split = s.asInstanceOf[CoGroupPartition]
  val numRdds = dependencies.length

这个rddIterators中第一个参数是对应上层依赖RDD中此Partition的Iterator,第二个参数是对应上层依赖的rdd的index.
  // A list of (rdd iterator, dependency number) pairs
  val rddIterators = new ArrayBuffer[(Iterator[Product2[KAny]], Int)]

这里得到每个依赖的Dependency与对应数组的index.
  for ((depdepNum) <- dependencies.zipWithIndex) dep match {

如果对应此依赖的RDD与当前的RDD的partitioner是相同的partitioner实例时,表示上层的shuffle与这个RDD的shuffle的算子相同,这个时候,直接得到对应的依赖的对应的Partition,并通过上层依赖的RDD的iterator来得到上层的依赖的RDD对应此PARTITION的Iterator的实例.
    case oneToOneDependency: OneToOneDependency[Product2[KAny]] @unchecked =>
      val dependencyPartition = split.narrowDeps(depNum).get.split
      // Read them from the parent
      val it = oneToOneDependency.rdd.iterator(dependencyPartitioncontext)
      rddIterators += ((itdepNum))

如果上层依赖是一个shuffle的依赖,这个时候表示当前的RDD的partitioner与上层的partitioner是不相同的shuffle算子,这个RDD的执行时,上层依赖的rdd stage已经执行完成,通过shuffleManager与当前shuffle过来的partition的index,得到这个shuffle结果的reader并生成Iterator的实例.先要执行shuffle的结果的读操作.
    case shuffleDependency: ShuffleDependency[___] =>
      // Read map outputs of shuffle
      val it = SparkEnv.get.shuffleManager
        .getReader(shuffleDependency.shuffleHandlesplit.indexsplit.index 1

            context)
        .read()
      rddIterators += ((itdepNum))
  }

这个步骤执行合并操作.

这个合并操作需要注意的是,针对相同的key进行合并,value是一个Array,这个array的长度就是这个rdd的上层依赖rdd的个数,也就是两个.如果数组中某一个下标没有存储值,表示这个key在另一个rdd中不存在,这里的key需要注意的是,是原始的RDD的kv代表着这个RDD的上层依赖的KEY.
  val map = createExternalMap(numRdds)
  for ((itdepNum) <- rddIterators) {
    map.insertAll(it.map(pair => (pair._1new CoGroupValue(pair._2depNum))))
  }
  context.taskMetrics().incMemoryBytesSpilled(map.memoryBytesSpilled)
  context.taskMetrics().incDiskBytesSpilled(map.diskBytesSpilled)
  context.internalMetricsToAccumulators(
    InternalAccumulator.PEAK_EXECUTION_MEMORY).add(map.peakMemoryUsedBytes)
  new InterruptibleIterator(context,
    map.iterator.asInstanceOf[Iterator[(KArray[Iterable[_]])]])
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值