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(sc, Array(this, other))
} else {
new UnionRDD(sc, Array(this, other))
}
}
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
(0 until numPartitions).map(index => {
这里根据partitioner的新的partition的个数,重新生成新的Partition,每个partition对应着两个上层的RDD对应的Partition.也就是说,这个新生成的Partition内部的parents包含有两个上层的两个rdd中对应此Partition的数组.
new PartitionerAwareUnionRDDPartition(rdds, index)
}).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 (rdd, part) => {
这里取出这个rdd中这个partition对应的hostname
val parentLocations = currPrefLocs(rdd, part)
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: Partition, context: TaskContext): Iterator[T] = {
val parentPartitions =
s.asInstanceOf[PartitionerAwareUnionRDDPartition].parents
rdds.zip(parentPartitions).iterator.flatMap {
case (rdd, p) => rdd.iterator(p, context)
}
}
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 ((rdd, rddIndex) <- rdds.zipWithIndex; split <- 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: Partition, context: TaskContext): Iterator[T] = {
val part = s.asInstanceOf[UnionPartition[T]]
parent[T](part.parentRddIndex).iterator(part.parentPartition, context)
}