spark的宽窄依赖,容错,stage划分,再也不怕面试了

1.宽窄依赖

 


图中左边是宽依赖,父RDD的4号分区数据划分到子RDD的多个分区(一分区对多分区),这就表明有shuffle过程,父分区数据经过shuffle过程的hash分区器(也可自定义分区器)划分到子RDD。例如GroupByKey,reduceByKey,join,sortByKey等操作。

图右边是窄依赖,父RDD的每个分区的数据直接到子RDD的对应一个分区(一分区对一分区),例如1号到5号分区的数据都只进入到子RDD的一个分区,这个过程没有shuffle。Spark中Stage的划分就是通过shuffle来划分。(shuffle可理解为数据的从原分区打乱重组到新的分区)如:map,filter

总结:如果父RDD的一个Partition被一个子RDD的Partition所使用就是窄依赖,否则的话就是宽依赖。

 

2.宽窄依赖&&容错性


Spark基于lineage的容错性是指,如果一个RDD出错,那么可以从它的所有父RDD重新计算所得,如果一个RDD仅有一个父RDD(即窄依赖),那么这种重新计算的代价会非常小。

Spark基于Checkpoint(物化)的容错机制何解?在上图中,宽依赖得到的结果(经历过Shuffle过程)是很昂贵的,因此,Spark将此结果物化到磁盘上了,以备后面使用

对于join操作有两种情况,如果join操作的每个partition 仅仅和已知的Partition进行join,此时的join操作就是窄依赖;其他情况的join操作就是宽依赖;因为是确定的Partition数量的依赖关系,所以就是窄依赖,得出一个推论,窄依赖不仅包含一对一的窄依赖,还包含一对固定个数的窄依赖(也就是说对父RDD的依赖的Partition的数量不会随着RDD数据规模的改变而改变)

3.Stage的划分


名词解析


1.一个 job,就是由一个 rdd 的 action 触发的动作,可以简单的理解为,当你需要执行一个 rdd 的 action 的时候,会生成一个 job。

2.stage : stage 是一个 job 的组成单位,就是说,一个 job 会被切分成 1 个或 1 个以上的 stage,然后各个 stage 会按照执行顺序依次执行。

3.task :即 stage 下的一个任务执行单元,一般来说,一个 rdd 有多少个partition,就会有多少个 task,因为每一个 task 只是处理一个partition 上的数据。

划分规则


1.从后向前推理,遇到宽依赖就断开,遇到窄依赖就把当前的RDD加入到Stage中;

2.每个Stage里面的Task的数量是由该Stage中最后 一个RDD的Partition数量决定的;

3.最后一个Stage里面的任务的类型是ResultTask,前面所有其他Stage里面的任务类型都是ShuffleMapTask;

4.代表当前Stage的算子一定是该Stage的最后一个计算步骤;

总结:由于spark中stage的划分是根据shuffle来划分的,而宽依赖必然有shuffle过程,因此可以说spark是根据宽窄依赖来划分stage的。

Spark优化


窄依赖对优化很有利。逻辑上,每个RDD的算子都是一个fork/join(此join非上文的join算子,而是指同步多个并行任务的barrier):把计算fork到每个分区,算完后join,然后fork/join下一个RDD的算子。如果直接翻译到物理实现,是很不经济的:一是每一个RDD(即使 是中间结果)都需要物化到内存或存储中,费时费空间;二是join作为全局的barrier,是很昂贵的,会被最慢的那个节点拖死。如果子RDD的分区到 父RDD的分区是窄依赖,就可以实施经典的fusion优化,把两个fork/join合为一个;如果连续的变换算子序列都是窄依赖,就可以把很多个 fork/join并为一个,不但减少了大量的全局barrier,而且无需物化很多中间结果RDD,这将极大地提升性能。Spark把这个叫做流水线(pipeline)优化。

Spark流水线优化:

变换算子序列一碰上shuffle类操作,宽依赖就发生了,流水线优化终止。在具体实现 中,DAGScheduler从当前算子往前回溯依赖图,一碰到宽依赖,就生成一个stage来容纳已遍历的算子序列。在这个stage里,可以安全地实施流水线优化。然后,又从那个宽依赖开始继续回溯,生成下一个stage。

Pipeline
在spark中pipeline是一个partition对应一个partition,所以在stage内部只有窄依赖。pipeline详解

stage与stage之间是宽依赖

4. 分布式计算过程


上图是一个Spark的wordcount例子,根据上述stage划分原则,这个job划分为2个stage,有三行,分别是数据读取、计算和存储过程。

仅看代码,用户根本体会不到数据在背后是并行计算。从图中能看出数据分布在不同分区(也可以理解不同机器上),数据经过flapMap、map和reduceByKey算子在不同RDD的分区中流转。(这些算子就是上面所说对RDD进行计算的函数)

下图从更高角度看:

Spark的运行架构由Driver(可理解为master)和Executor(可理解为worker或slave)组成,Driver负责把用户代码进行DAG切分,划分为不同的Stage,然后把每个Stage对应的task调度提交到Executor进行计算,这样Executor就并行执行同一个Stage的task。

(这里Driver和Executor进程一般分布在不同机器上)

这里有人可能不理解Stage和task,下图就是Spark的作业划分层次:

Application就是用户submit提交的整体代码,代码中又有很多action操作,action算子把Application划分为多个job,job根据宽依赖划分为不同Stage,Stage内划分为许多(数量由分区决定,一个分区的数据由一个task计算)功能相同的task,然后这些task提交给Executor进行计算执行,把结果返回给Driver汇总或存储。

这体现了 Driver端总规划–Executor端分计算–结果最后汇总回Driver 的思想,也就是分布式计算的思想。
 

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 创作都市 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读