一:RDD的依赖关系
1.在代码中观察
val data = Array(1, 2, 3, 4, 5)
val distData = sc.parallelize(data)
val resultRDD = distData.flatMap(v => (1 to v)).map(v => (v%2,1)).reduceByKey(_+_)
resultRDD.toDebugString ## 查看RDD的依赖情况
2.解释
+ —处表示,这是两个不同的stage
同时可以知道shuffledRDD依赖于MapPartitionRDD,MapPartitionRDD依赖于MapPartitionRDD,MapPartitionRDD依赖于ParalleCollectionRDD
[2]表示有两个分区
3.RDD依赖
lineage: 生命线
依赖于RDD之间的依赖,后续的RDD数据是从之前的RDD中获取
由于存在RDD的依赖,当一个后续的RDD执行失败的情况下(某个Task执行失败,eg:数据丢失),可以从父RDD中重新执行
RDD依赖父RDD,依赖的父RDD可以有多个;
特例:第一个RDD是没有父RDD的
RDD的内部是由多个Partiiton构成的,所以RDD的依赖实质上就是RDD中Partition的依赖关系
4.依赖的情况
当前RDD中的每个分区的数据到下一个RDD都对应一个分区
即:一个分区的数据输出到下一个RDD的时候还是在同一个分区,也就是一对一
当前RDD中的多个分区的数据到下一个RDD的时候输出到同一个分区,当前RDD的中一个分区的数据到下一个RDD的时候输出到多个分区,也就是多对多
5.依赖分类
窄依赖:
子RDD中的每个分区的数据都来自于常数个父RDD的分区,而且父RDD每个分区的数据到子RDD的时候一定在一个分区中
不存在shuffle过程,所有操作在一起进行
宽依赖:
子RDD中的每个分区的数据都依赖所有父RDD的所有的分区数据,而且父RDD的每个分区的数据到子RDD的时候不一定在一个分区中
存在shuffle过程,需要等待上一个RDD的所有Task执行完成
注意点:
join有时候是宽依赖,有时候是窄依赖,这个要看分区数量会不会改变。
6.算子与依赖之间的关系
原本以为Transformation的算子是窄依赖,Action算子是宽依赖。
现在理解更深了一下,发现他们是两个概念,不要混淆。
二:stage的划分
1.Spark Application Job的Stage划分规则
RDD在调用transformation类型的函数时候形成DAG执行图(RDD的依赖)
RDD在调用action类型函数的时候会触发job的执行
在Driver中使用DAGScheduler对DAG图进行Stage的划分
从DAG图的最后一步(结果输出的那一步)往前推,如果发现API是宽依赖(ShuffledRDD), 就结束推断,将此时构成的DAG图称为一个Stage,然后继续往前推断,直到第一个RDD
====> Stage与Stage之间的分割是宽依赖
三:两种RDD依赖的复习
1.说明
主要是添加一个知识点。
什么情况下父RDD需要执行。