RDD 的成员之一是依赖集,依赖集也关系到任务调度

  源码

  Dependency代码主要在一个源文件中:core/Dependency代码中有5个类。

  除此以外在 core/rdd/PartitionPruningRDD还有一个PruneDependency类。

  他们的名称和继承关系如下图:

Spark源码分析:RDD的依赖_第1张图片

  通过阅读代码可以得到以下信息:

  依赖的根类是Dependency,只有一个RDD 成员,表示依赖的对象。这类继承了Serializable类,是可以序列化的。

  依赖分为两大种,一个叫窄依赖(Narrow),另一个就洗牌依赖(Shuffle,很多材料也叫作“宽”依赖)。

  从数量关系上说,“1-to-1”(OneToOne)、“n-to-1”(Range)、“1-to-部分分区”(Prune,剪枝)是窄依赖,宽依赖是“n-to-n”的。

  “1-to-1”是RDD的默认依赖。上节中的MapPartitionRDD是一对一的转换,就包含“1-to-1“依赖。

  ”n-to-1“的依赖只有一个使用场景——UnionRDD,“交”运算,多个 RDD 合并到一个 RDD 中。

  剪枝依赖是个私有对象,用于优化,减少数据载入。

  洗牌依赖复杂一些。

  只有 RDD 的转换(Transformations)才用到依赖,RDD 的操作(Actions,如reduce、collect等)就不需要依赖,直接运行SparkContext.runjob()函数。RDD 有哪些转换和操作?阅读文档: Spark Programming Guide - Spark 2.0.2 Documentation

  洗牌依赖

  洗牌依赖也只是相对复杂,代码也不长。

Spark源码分析:RDD的依赖_第2张图片

  是的,只有8行代码,核心内容是:

  这个类有三个泛型类型,K=key,V=value,C=combiner;

  洗牌依赖只能用于Product2[K, V]及其父类,即 KV 数据;

  成员有 分区器(partitioner) 、序列器(serializer)、排序器(keyOrdering)、聚合器(aggregator)、map 端聚合开关(mapSideCombine);

  _rdd.context.newShuffleId() 获得一个自增的 ID;

  _rdd.context.env.shuffleManager.registerShuffle 获得几个洗牌的句柄。通过core/shuffle/sort/SortShuffleManager代码可以知道,一共有三种句柄:

  分区数很少(小于变量spark.shuffle.sort.bypassMergeThreshold,默认200)时,用BypassMergeSortShuffleHandle,直接发送数据合并,不用耗时的序列化和反序列化;

  否则,如果能序列化,则用SerializedShuffleHandle,用序列化和反序列化,降低网络 IO;

  否则,使用基础的BaseShuffleHandle。

  Scala语法

  在 Java 中使用null 是非常容易出错的,在 Guava( GitHub - google/guava: Google Core Libraries for Java 6+ ) 中提供了 Optional 来避免使用 null。

  同样的 Scala 自带了 Option 来避免使用 null。

  Option 有两个子类,Some 和 None

Spark源码分析:RDD的依赖_第3张图片

  None.get 会报错,用 getOrElse 可以给取不到值的时候赋默认值:

Spark源码分析:RDD的依赖_第4张图片

  在 Spark 中大量使用了 Option。

  疑问列表

  我将阅读过程中的未解内容记录下来,留待以后阅读代码时解答。疑问一个一个划掉,就是成长的过程。

  reduce 等 RDD 操作是如何执行的?

  groupByKey 等洗牌操作是如何执行的?不同的洗牌类型有什么用?

  结论

  在 RDD 的转换时,会用到依赖

  依赖包括窄依赖(1-to-1、n-to-1关系)、洗牌依赖(n-to-n 关系)

  洗牌依赖包含分区器、序列器、排序器、聚合器、map聚合开关、ID、洗牌类型句柄等成分组成。洗牌类型句柄有三种。