spark transform系列__union

Union

Union的transform主要是把两个RDD合并成一个RDD的动作,在union的操作中,如果要进行合并的两个rdd的partitioner的算子实例是同一个实例时,表示这两个rdd有相同的分区方法,合并后的RDD为PartitionerAwareUnionRDD实例.否则生成UnionRDD实例.

 

Union的实现代码:

def union(other: RDD[T]): RDD[T] = withScope {
  if (partitioner.isDefined && other.partitioner == partitioner) {
    new PartitionerAwareUnionRDD(scArray(thisother))
  } else {
    new UnionRDD(scArray(thisother))
  }
}

 

PartitionerAwareUnionRDD的实例逻辑:

如果要进行合并的两个RDD都包含有partitioner,同时这两个RDD引用的是相同的partitioner时,返回的UNIONRDD是PartitionerAwareUnionRDD的实例.

 

实例生成:

这里根据两个RDD,在新的RDD中直接根据每一个RDD生成OneToOneDependency的实例,两个RDD最终生成一个上层RDD依赖的集合,下面的红色部分生成的OneToOneDependency.

class PartitionerAwareUnionRDD[T: ClassTag](
    sc: SparkContext,
    var rdds: Seq[RDD[T]]
  ) extends RDD[T](sc, rdds.map(x => new OneToOneDependency(x)))

 

对partitions的重写:

由于包含有Partitioner的算子,因此这个RDD的结果输入时,对应的partition的个数由Partitioner算子来决定,两个合并的RDD是相同的partitioner的算子(如hash,那么相同的key的hash值相同的都会到同一个partition中),

override def getPartitions: Array[Partition] = {
  val numPartitions = partitioner.get.numPartitions
  (until numPartitions).map(index => {

这里根据partitioner的新的partition的个数,重新生成新的Partition,每个partition对应着两个上层的RDD对应的Partition.也就是说,这个新生成的Partition内部的parents包含有两个上层的两个rdd中对应此Partition的数组.
    new PartitionerAwareUnionRDDPartition(rddsindex)
  }).toArray
}

 

getPreferredLocations重写:

这个地方主要是提取出上层RDD的依赖对应的partition中对应的host,取出上层的两个RDD中,对应此partition中对应host中被引用最大的那一个host的值,来当这个RDD当,对应的partition的host.

 

这行代码得到当前的partition对应的两个上层RDD对应的partition的数组.

val parentPartitions = s.asInstanceOf[PartitionerAwareUnionRDDPartition].parents

 

Rdds.zip是把两个数组进行合并,合并后一个元素对应的位置中第一个值为rdd,第二个值为rdd对应的partition.
val locations = rdds.zip(parentPartitions).flatMap {
  case (rddpart) => {

这里取出这个rdd中这个partition对应的hostname
    val parentLocations = currPrefLocs(rddpart)
    logDebug("Location of " + rdd + " partition " + part.index + " = " + parentLocations)
    parentLocations
  }
}
val location = if (locations.isEmpty) {
  None
else {

这里根据hostname进行分组,取出hostname被使用最高的这个hostname,就是当前的partition的location.
  // Find the location that maximum number of parent partitions prefer
  Some(locations.groupBy(x => x).maxBy(_._2.length)._1)
}
logDebug("Selected location for " this ", partition " + s.index + " = " + location)
location.toSeq

 

 

对compute的重写:

这个地方直接调用上层的依赖的两个RDD的对应的parition的iterator,并把两个iterator的结果生成为一个iterator.这里的flatMap是把两个RDD中相同的partition的iterator进行首尾相连.

先迭代第一个rdd中对应的partition,然后接着迭代第一个rdd中对应的partition.

override def compute(s: Partitioncontext: TaskContext): Iterator[T] = {
  val parentPartitions = 

       s.asInstanceOf[PartitionerAwareUnionRDDPartition].parents
  rdds.zip(parentPartitions).iterator.flatMap {
    case (rddp) => rdd.iterator(pcontext)
  }
}

 

 

UnionRDD的实例逻辑:

如果要合并的两个RDD中,有一个RDD不包含partitioner或者说,两个RDD的partitioner的算子不相同时,这个时候对两个RDD的union操作会生成一个UnionRDD的实例.

 

实例的生成部分代码:

在实例生成时,对上层依赖的Dependency默认的传入参数是一个Nil,表示开始生成UnionRDD时,不处理对上层的RDD的依赖.看下面代码的红色部分.这部分的RDD的上层依赖是一个较特别的部分,会单独生成.

class UnionRDD[T: ClassTag](
    sc: SparkContext,
    var rdds: Seq[RDD[T]])
  extends RDD[T](sc, Nil) {

 

 

处理UnionRDD的上层RDD的依赖部分代码:

override def getDependencies: Seq[Dependency[_]] = {
  val deps = new ArrayBuffer[Dependency[_]]
  var pos = 0
  for (rdd <- rdds) {

这里生成的上层RDD依赖部分已经不在是OneToOneDependency,而是RangeDependency的依赖,

生成RangeDependency的依赖时,pos表示对应上层RDD的INDEX,

比如:rdd1对应的parttion的个数为10,rdd2对应的partition的个数为20,

那以生成的第一个RangeDependency的参数(rdd1,0,0,10),

生成的第二个RangeDependency的参数(rdd2,0,10,20),

这里面生成的值与partitions配合进行使用.
    deps += new RangeDependency(rdd, 0, pos, rdd.partitions.length)
    pos += rdd.partitions.length
  }
  deps
}

 

 

处理UnionRDD的getPartitions函数的部分逻辑:

override def getPartitions: Array[Partition] = {

这里针对UnionRDD的partition的个数为依赖的上层两个RDD的partition个数的总和
  val array = new Array[Partition](rdds.map(_.partitions.length).sum)
  var pos = 0

这里执行的迭代是一个双层迭代,第一层迭代先得到上层依赖的一个RDD,第二层迭代得到这个RDD中的每一个PARTITION的INDEX.
  for ((rddrddIndex) <- rdds.zipWithIndexsplit <- rdd.partitions) {

根据迭代的RDD,pos变量是当前迭代到的所有的partition的下标,split.index是当前的rdd对应的partition.
    array(pos) = new UnionPartition(pos, rdd, rddIndex, split.index)
    pos += 1
  }
  array
}

 

处理UnionRDD的compute函数的部分逻辑:

在UnionRDD的compute函数中,处理相对是一个比较简单的处理方法,直接拿到当前的UnionRDD中对应的partition,找到这个partition对应的上层依赖的rdd,同时找到这个partition对应上层RDD的partition,直接调用这个RDD的iterator的函数.这个不同于PartitionerAwareUnionRDD需要把从个Partition进行flatMap

....代码.

override def compute(s: Partitioncontext: TaskContext): Iterator[T] = {
  val part = s.asInstanceOf[UnionPartition[T]]
  parent[T](part.parentRddIndex).iterator(part.parentPartitioncontext)
}

你可能感兴趣的:(spark,源代码)