sparkRDD依赖
RDD的最重要的特性之一就是血缘关系,血缘关系描述了一个RDD是如何从父RDD计算得来的。其中Dependency的rdd方法返回一个RDD,及所依赖的RDD.
abstract class Dependency[T] extends Serializable { def rdd: RDD[T] }Dependency分为两种, narrow和shuffle
先看看比较简单的narrow
定义, parent RDD中的每个partition最多被child RDD中的一个partition使用, 即不需要shuffle
更直白点, 就是Narrow只有map, partition本身范围不会改变, 一个parititon经过transform还是一个partition, 虽然内容发生了变化, 所以可以在local完成
而wide就是, partition需要打乱从新划分, 存在shuffle的过程, partition的数目和范围都发生了变化
唯一的接口getParents, 即给定任一个partition-id, 得到所有依赖的parent partitions的id的seq
@DeveloperApi abstract class NarrowDependency[T](_rdd: RDD[T]) extends Dependency[T] { /** * Get the parent partitions for a child partition. * @param partitionId a partition of the child RDD * @return the partitions of the parent RDD that the child partition depends upon */ def getParents(partitionId: Int): Seq[Int] override def rdd: RDD[T] = _rdd }NarrowDependency又分为两种,
OneToOneDependency
最简单的依赖关系, 即parent和child里面的partitions是一一对应的, 典型的操作就是map, filter…
其实partitionId就是partition在RDD中的序号, 所以如果是一一对应, 那么parent和child中的partition的序号应该是一样的
RangeDependency
虽然仍然是一一对应, 但是是parent RDD中的某个区间的partitions对应到child RDD中的某个区间的partitions
典型的操作是union, 多个parent RDD合并到一个child RDD, 故每个parent RDD都对应到child RDD中的一个区间
需要注意的是, 这里的union不会把多个partition合并成一个partition, 而是的简单的把多个RDD中的partitions放到一个RDD里面, partition不会发生变化
由于是range, 所以直接记录起点和length就可以了, 没有必要加入每个中间rdd, 所以RangeDependency优化了空间效率
/** * Represents a one-to-one dependency between ranges of partitions in the parent and child RDDs. * @param rdd the parent RDD * @param inStart the start of the range in the parent RDD, parent RDD中区间的起始点 * @param outStart the start of the range in the child RDD, child RDD中区间的起始点 * @param length the length of the range */ class RangeDependency[T](rdd: RDD[T], inStart: Int, outStart: Int, length: Int) extends NarrowDependency[T](rdd) { override def getParents(partitionId: Int) = { if (partitionId >= outStart && partitionId < outStart + length) { //判断partitionId的合理性,必须在child RDD的合理partition范围内 List(partitionId - outStart + inStart) //算出parent RDD中对应的partition id } else { Nil } } }
WideDependency, 也称为ShuffleDependency
首先需要基于PairRDD, 因为一般需要依据key进行shuffle, 所以数据结构往往是kv
即RDD中的数据是kv pair, [_ <: Product2[K, V]],
trait Product2[+T1, +T2] extends Product // Product2 is a cartesian product of 2 components
Product2是trait, 这里实现了Product2可以用于表示kv pair? 不是很理解
其次, 由于需要shuffle, 所以当然需要给出partitioner, 如何完成shuffle
然后, shuffle不象map可以在local进行, 往往需要网络传输或存储, 所以需要serializerClass
最后, 每个shuffle需要分配一个全局的id, context.newShuffleId()的实现就是把全局id累加
/** * :: DeveloperApi :: * Represents a dependency on the output of a shuffle stage. Note that in the case of shuffle, * the RDD is transient since we don't need it on the executor side. * * @param _rdd the parent RDD * @param partitioner partitioner used to partition the shuffle output * @param serializer [[org.apache.spark.serializer.Serializer Serializer]] to use. If not set * explicitly then the default serializer, as specified by `spark.serializer` * config option, will be used. * @param keyOrdering key ordering for RDD's shuffles * @param aggregator map/reduce-side aggregator for RDD's shuffle * @param mapSideCombine whether to perform partial aggregation (also known as map-side combine) */ @DeveloperApi class ShuffleDependency[K: ClassTag, V: ClassTag, C: ClassTag]( @transient private val _rdd: RDD[_ <: Product2[K, V]], val partitioner: Partitioner, val serializer: Serializer = SparkEnv.get.serializer, val keyOrdering: Option[Ordering[K]] = None, val aggregator: Option[Aggregator[K, V, C]] = None, //聚合器。 val mapSideCombine: Boolean = false) extends Dependency[Product2[K, V]] { override def rdd: RDD[Product2[K, V]] = _rdd.asInstanceOf[RDD[Product2[K, V]]] private[spark] val keyClassName: String = reflect.classTag[K].runtimeClass.getName private[spark] val valueClassName: String = reflect.classTag[V].runtimeClass.getName // Note: It's possible that the combiner class tag is null, if the combineByKey // methods in PairRDDFunctions are used instead of combineByKeyWithClassTag. private[spark] val combinerClassName: Option[String] = Option(reflect.classTag[C]).map(_.runtimeClass.getName) val shuffleId: Int = _rdd.context.newShuffleId() val shuffleHandle: ShuffleHandle = _rdd.context.env.shuffleManager.registerShuffle( shuffleId, _rdd.partitions.length, this) _rdd.sparkContext.cleaner.foreach(_.registerShuffleForCleanup(this)) }