1.前言


《上一节Spark DAG概述》Spark中RDD的高效与DAG图有着莫大的关系,在DAG调度中需要对计算过程划分stage, 暴力的理解就是stage的划分是按照有没有涉及到shuffle来划分的,没涉及的shuffle的都划分在一个stage里面, 这种划分依据就是RDD之间的依赖关系。针对不同的转换函数,RDD之间的依赖关系分类窄依赖(narrow dependency) 和宽依赖(wide dependency, 也称 shuffle dependency)



2.定义



1.窄依赖是指父RDD的每个分区只被子RDD的一个分区所使用,子RDD分区通常对应常数个父RDD分区(O(1),与数据规模无关) 2.相应的,宽依赖是指父RDD的每个分区都可能被多个子RDD分区所使用,子RDD分区通常对应所有的父RDD分区(O(n),与数据规模有关) 3.宽依赖和窄依赖关系图:



3.为什么要有宽窄依赖?



1.前面已经说过了stage划分的一个很重要的原因就是有没有涉及到shuffle,如果没涉及到的被划分到一个stage里面。 2.没有涉及shuffle的任务直接运行就可以,这个也就是常提到的pipeline。这种面向的就是窄依赖。     √ 每个分区里的数据都被加载到机器的内存里,我们逐一的调用 map, filter, map 函数到这些分区里,Job 就很好的完成。     √ 更重要的是,由于数据没有转移到别的机器,我们避免了 Network IO 或者 Disk IO.     唯一的任务就是把 map / filter 的运行环境搬到这些机器上运行,这对现代计算机来说,overhead 几乎可以忽略不计。     √ 这种把多个操作合并到一起,在数据上一口气运行的方法在 Spark 里叫 pipeline     (其实 pipeline 被广泛应用的很多领域,比如 CPU)。     这时候不同就出现了:只有 narrow transformation 才可以进行 pipleline 操作。     对于 wide transformation, RDD 转换需要很多分区运算,包括数据在机器间搬动,所以失去了 pipeline 的前提。     √ 总结起来一句话:数据和算是否在一起,计算的性能是不一样的,为了区分,就有了宽依赖和窄依赖。 3.一提到shuffle如果之前对mapreduce有过了解的人都知道,这个对分布式影响巨大, spark也是一步步演变过来的,现在可以说spark2.x以上的shuffle可以认为和经典的mapreduce的shuffle一样了,到现在可以 说spark完全比mr有优势了。这之前,在一些场景下spark还是比不过mr的。(看这篇《Spark 与MapReduce 资源调度方面的简单对比》) 4.宽窄依赖如何优化?----得想想



4.窄依赖对优化的帮助



1.宽依赖往往对应着shuffle操作,需要在运行过程中将同一个父RDD的分区传入到不同的子RDD分区中,中间可能涉及到多个节点之间的   数据传输;而窄依赖的每个父RDD的分区只会传入到一个子RDD分区中,通常可以在一个节点内就可以完成了。 2.当RDD分区丢失时(某个节点故障),spark会对数据进行重算。 1).对于窄依赖,由于父RDD的一个分区只对应一个子RDD分区,这样只需要重算和子RDD分区对应的父RDD分区即可,     所以这个重算对数据的利用率是100%的; 2).对于宽依赖,重算的父RDD分区对应多个子RDD分区,这样实际上父RDD 中只有一部分的数据是被用于恢复这个丢失的子RDD     分区的,另一部分对应子RDD的其它未丢失分区,这就造成了多余的计算;更一般的,宽依赖中子RDD分区通常来自多个父RDD     分区,极端情况下,所有的父RDD分区都要进行重新计算。 3.如下图所示,b1分区丢失,则需要重新计算a1,a2和a3,这就产生了冗余计算(a1,a2,a3中对应b2的数据)



区分这两种依赖很有用。首先,窄依赖允许在一个集群节点上以流水线的方式(pipeline)计算所有父分区。 例如,逐个元素地执行map、然后filter操作; 而宽依赖则需要首先计算好所有父分区数据,然后在节点之间进行Shuffle,这与MapReduce类似。 第二,窄依赖能够更有效地进行失效节点的恢复,即只需重新计算丢失RDD分区的父分区,而且不同节点之间可以并行计算; 而对于一个宽依赖关系的Lineage图,单个节点失效可能导致这个RDD的所有祖先丢失部分分区,因而需要整体重新计算。 窄依赖中每个子RDD可能对应多个父RDD,当子RDD丢失时会导致多个父RDD进行重新计算,所以窄依赖不如宽依赖有优势。 而实际上应该深入到分区级别去看待这个问题,而且重算的效用也不在于算的多少,而在于有多少是冗余的计算。 窄依赖中需要重算的都是必须的,所以重算不冗余 窄依赖的函数有:map, filter, union, join(父RDD是hash-partitioned ), mapPartitions, mapValues 宽依赖的函数有:groupByKey, join(父RDD不是hash-partitioned ), partitionBy