在进行本篇之前,如果还有对Spark的一些基础概念不太明白的可以参考一下这篇博文:
Spark核心组件、运行架构
RDD的依赖
RDD是一种弹性分布式数据集,我们以图示的形式来展示一下它的原理:
RDD的宽窄依赖
Lineage:血统、遗传
- RDD最重要的特性之一,保存了RDD的依赖关系;
- RDD实现了基于Lineage的容错机制;
依赖关系
宽依赖:一个父RDD的分区被子RDD的多个分区使用;
窄依赖:一个父RDD的分区被子RDD的一个分区使用;
例如union:
常见的宽依赖有:groupByKey,partitionBy,reduceByKey,Join(父RDD不是hash-partitioned :除此之外的,rdd 的join api是宽依赖)。
常见的窄依赖有:map,filter,union,mapPartitions,mapValues,join(父RDD是hash-partitioned :如果JoinAPI之前被调用的RDD API是宽依赖(存在shuffle), 而且两个join的RDD的分区数量一致,join结果的rdd分区数量也一样,这个时候join api是窄依赖)。
宽依赖对比窄依赖
- 宽依赖对应shuffle操作,需要在运行时将同一个父RDD的分区传入到不同的子RDD分区中,不同的分区可能位于不同的节点,就可能涉及多个节点间数据传输
- 当RDD分区丢失时,Spark会对数据进行重新计算,对于窄依赖只需重新计算一次子RDD的父RDD分区。相比于宽依赖,窄依赖对优化更有利。
DAG工作原理
根据RDD之间的依赖关系,形成一个DAG(有向无环图)。
DAGScheduler将DAG划分为多个Stage
- 划分依据:是否发生宽依赖(Shuffle)
- 划分规则:从后往前,遇到宽依赖切割为新的Stage
- 每个Stage由一组并行的Task组成
DAGScheduler
有向无环图调度器。
- 基于DAG划分Stage 并以TaskSet的形势提交Stage给TaskScheduler;
- 负责将作业拆分成不同阶段的具有依赖关系的多批任务;
- 最重要的任务之一就是:计算作业和任务的依赖关系,制定调度逻辑。
- 在SparkContext初始化的过程中被实例化,一个SparkContext对应创建一个DAGScheduler。
TaskSceduler:
任务调度器。将TaskSet提交给Worker(集群)运行并返回结果;负责每个具体任务的实际物理调度。
下面来张总表供大家仔细查看:
划分Stage的原因
数据本地化
- 移动计算,而不是移动数据;
-保证一个Stage内不会发生数据移动;
建议
Spark Shuffler过程
在分区之间重新分配数据
- 父RDD中同一分区中的数据按照算子要求重新进入子RDD的不同分区中
- 中间结果写入磁盘
- 由子RDD拉取数据,而不是由父RDD推送
- 默认情况下,Shuffle不会改变分区数量