(spark源码)union算子

摘要
  1. 问题: spark中, union方法是否重新分区, 是否会触发shuffle
  2. 结论: 不会shuffle, 不会划分stage, 但是可能重新分区(窄依赖)
  3. 解释:
    (1) 宽窄依赖对应的原称为ShuffleDependencyNarrowDependency, 字面上可以看出来, 只有宽依赖才会发生shuffle. 但是两种依赖都会重新分区, 因此重分区和是否shuffle没有关系
    (2) 如果被union的多个rdd, 分区规则相同, 那么index相同的分区, 会被整合到多数分区所在的节点. 比如a节点有2index0的分区, b节点有1个, union之后的0分区会全部转移到a节点
    (3) 如果分区规则不同, union后生成的UnionRDD, 不会进行重新分区, 而是把每个分区合并记录到分区数组中
union方法
// 调用sparkContent的方法, 把this作为参数传进去. 基本操作
def union(other: RDD[T]): RDD[T] = withScope {
  sc.union(this, other)
}
// 实际调用了重载方法
def union[T: ClassTag](first: RDD[T], rest: RDD[T]*): RDD[T] = withScope {
  union(Seq(first) ++ rest)
}
// 解析分区器, 执行不同逻辑
def union[T: ClassTag](rdds: Seq[RDD[T]]): RDD[T] = withScope {
  // 把每个rdd的分区器取出来, 放到Set中去重
  val partitioners = rdds.flatMap(_.partitioner).toSet
  // 如果每个rdd都定义了分区器, 而且全部相同, 就使用此分区器整合所有rdd
  if (rdds.forall(_.partitioner.isDefined) && partitioners.size == 1) {
    new PartitionerAwareUnionRDD(this, rdds)
  } else {
    // 否则就保留各自的分区
    new UnionRDD(this, rdds)
  }
}

PartitionerAwareUnionRDD

该类中, 数据重新分区, 但是是窄依赖

class PartitionerAwareUnionRDD[T: ClassTag](
    sc: SparkContext,
    var rdds: Seq[RDD[T]]
    // 注意这个继承, OneToOneDependency是窄依赖
  ) extends RDD[T](sc, rdds.map(x => new OneToOneDependency(x))) {
  // 因为保证了全部rdd的分区器一样, 所以head取第一个即可
  override val partitioner = rdds.head.partitioner
  // 整合rdds的分区
  override def getPartitions: Array[Partition] = {
    // 获取分区数
    val numPartitions = partitioner.get.numPartitions
    (0 until numPartitions).map { index =>
      // 将分区抽象为对象, 该类表示分区
      new PartitionerAwareUnionRDDPartition(rdds, index)
    }.toArray
  }
}

UnionRDD

class UnionRDD[T: ClassTag](
    sc: SparkContext,
    var rdds: Seq[RDD[T]])
  // 传入的参数是空依赖, 但是自定义了依赖方法
  extends RDD[T](sc, Nil) {  // Nil since we implement getDependencies

  // 查看是否超出了并行列表阈值
  private[spark] val isPartitionListingParallel: Boolean =
    rdds.length > conf.getInt("spark.rdd.parallelListingThreshold", 10)

  override def getPartitions: Array[Partition] = {
    // 如果超出了并行列表阈值
    val parRDDs = if (isPartitionListingParallel) {
      // par方法, 用于获取和原集合并行的副本
      val parArray = rdds.par
      // 如果并行度太高, 就是靠线程池(fork join pool)拆分任务
      parArray.tasksupport = UnionRDD.partitionEvalTaskSupport
      parArray
    } else {
      rdds
    }
    
    // array的长度是rdds的分区数之和
    val array = new Array[Partition](parRDDs.map(_.partitions.length).seq.sum)
    var pos = 0
    // 双循环
    for ((rdd, rddIndex) <- rdds.zipWithIndex; split <- rdd.partitions) {
      // 参数是: (自增的角标, rdd, rdd在rdds中的角标, 原始分区的rdd的角标)
      // 根据这几个参数, 重新记录各分区. 实际上就是做了个和
      array(pos) = new UnionPartition(pos, rdd, rddIndex, split.index)
      pos += 1
    }
    array
  }
  
  // 自定义的获取依赖的方法
  override def getDependencies: Seq[Dependency[_]] = {
    val deps = new ArrayBuffer[Dependency[_]]
    var pos = 0
    for (rdd <- rdds) {
      // RangeDependency是窄依赖
      deps += new RangeDependency(rdd, 0, pos, rdd.partitions.length)
      pos += rdd.partitions.length
    }
    deps
  }
}
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值