spark宽窄依赖

422 阅读2分钟

spark根据宽窄依赖划分stage,那么如何区分宽窄依赖呢?

概念

很多资料上讲宽窄依赖:窄依赖指父RDD的每个分区只被子RDD的一个分区所使用,宽依赖指父RDD的每个分区都可能被子RDD的多个分区使用。这种说法是不严谨的。
spark join算子在两个rdd分区器相同的情况下是窄依赖,但是并不属于每个父rdd分区只被子RDD的一个分区所使用的情况。

image.png

真正严谨的描述是:如果子RDD的一个分区完全依赖父RDD的一个或多个分区的全部数据,则是窄依赖,否则就是宽依赖。这个完全依赖怎么理解呢?其实就是父RDD一个分区的数据是否需要切分。所以宽窄依赖本质上是父RDD是否需要重新划分分区,也就是分区器是否变化。

算子

算子分两类,一类会直接记录依赖关系,一类根据和父rdd的分区器是否一致判断

  1. 生成MapPartitionsRDD的操作都是OneToOneDependency,比如map flatmap filter
  2. union操作是RangeDependency
  3. join,cogroup,intersection,intersection,subtractByKey,substract 取决于分区器是否一致
  4. sortByKey、sortBy shuffleDependency
  5. combineByKeyWithClassTag 、combineByKey、aggregateByKey、foldByKey、reduceByKey、countApproxDistinctByKey、groupByKey、partitionBy 取决于分区器与父rdd是否一致
  6. coalesce shuffle为true的时候进行shuffle 为false的时候是窄依赖

pipline计算

同一个stage内的算子pipline计算,通过rdd之间的迭代器的嵌套,新的迭代器保存着对当前迭代器的引用从而形成链表,每个迭代器需要实现hasNext(),next()两个方法。当触发计算时,最后一个创建的迭代器会调用next方法,next方法会调用父迭代器的next方法, 形成一个函数链。 spark每个任务都是由向前依赖串联起来RDD链表生成的iterator链表构成的,任务执行由最后的一个iterator的迭代开始,调用上游的迭代器的next,直到迭代到第一个iterator。这样避免了将所有数据先加载到内存中,而每次计算都只从源头取一条数据,大大节省了内存。