spark-窄、宽依赖和任务划分

1,313 阅读3分钟

窄依赖和宽依赖

窄依赖

  • 每一个父RDD的Partition最多被子RDD的一个Partition使用
  • 独生子女:一个爹RDD只有一个子

宽依赖

  • 每一个父RDD的Partition被子RDD的多个Partition使用,伴随shuffle
  • 超生:一个爹RDD有多个子

个人理解

由于还没学shuffle,所以从宏观简单思考。学一个东西不能死记硬背,最好的理解就是:问问自己为啥要分窄依赖和宽依赖?

先分析例子:

  • 例1:用map时,一个分区里的数据经过函数,形成新的数据,大家你搞你的我搞我的,互不干扰。
  • 例2:用合并操作时,多个分区合到一个分区,同样,各走各的,顶走跑之前计算下新偏移量(这个偏移别人没跑完我也知道),也可以说是互不干扰
  • 例3:用groupbykey时,这下可不是互不干扰了,因为需要比较洗牌,你得等你的伙伴(另一个分区)算完了,才能执行groupbykey。

因此我觉得这就是所谓的宽依赖:别的分区没跑完,不能执行下一步,需要等待。只有当大家都准备好了,才可以一起进行洗牌。由于分区里的数据顺序之前是乱的,所以shuffle时一般都会拆开,然后送到不同的子分区。这就造成了结果——超生。说实话,如果你从结果出发去思考,是不好区分例2例3的。

接着,划分窄依赖(别的分区没跑完,可以执行下一步)和宽依赖(别的分区没跑完,不可以执行下一步)的原因显而易见。我们可以把窄依赖的步骤划分到一起,它可以一路执行,不需要等待,直到宽依赖步骤卡住(必须等其它分区执行完)。这个从窄依赖一路执行到宽依赖的过程,可以在逻辑上划分成一个stage。这也就是常说的宽依赖是划分Stage的依据

任务划分

RDD任务的切分,分为:Application、Job、Stage和Task,而且每一层都是1对n的关系

4个名词

  • Application:初始化一个SparkContext即生成一个Application
  • Job:一个Action算子就会生成一个Job
  • Stage:根据RDD之间的依赖关系的不同将Job划分成不同的Stage,遇到一个宽依赖则划分一个Stage。
  • Task:Stage是一个TaskSet,将Stage划分的结果发送到不同的Executor执行即为一个Task。

个人理解

同样思考为啥要划分这么多东西?

  • Application

一个spark不止跑一个程序吧,所以一个程序一个 Application理所当然,进而生成一个AppMaster管理它。

  • Job

一个程序有许多转换算子和行动算子。只有执行到行动操作才真正改变数据,所以把截止到行动算子的算子划一个job合情合理吧。而且我们从源码也可以看到,执行一个行动操作,就会执行sc.runJob(...)

  • Stage

在一个Job中,有的可一路执行到宽依赖的,不需要等待,按这个划分为一个Stage。这个不理解的再看看上面的分析。

  • Task

在一个Stage中,我们观察最后一组分区,也就是shuffer前的,由于到这里都是可以一路执行的,所以按最后一组分区的个数,一个分区划一个Task。此时都划到分区了,自然不用划分了。