Spark入门篇——RDD的血缘

目录

RDD的血缘

概述

划分依赖的背景

划分以来的依据

窄依赖

宽依赖

 join的依赖

宽依赖

 窄依赖

 依赖与Stage的划分

Stage的类别

 依赖与容错

转换算子中间发生失败

DAG的生成

总结


RDD的血缘

概述

RDD可以从本地集合并行化、从外部文件系统、其他RDD转化得到,能从其他RDD通过Transformation创建新的RDD的原因是RDD之间有依赖关系(Dependency代表了RDD之间的依赖关系,即血缘(Lineage)),RDD和他依赖的父RDD有两种不同的类型,即宽依赖(wide dependency)和窄依赖(narrow dependency)。

划分依赖的背景

  1. 从计算过程,窄依赖是数据以管道的方式经一系列计算操作可以运行在一个集群节点上,,例如:map、filter。宽依赖则可能需要将数据跨节点传输后运行(如groupByKey),有点类似于MR的shuffle过程。
  2. 从失败恢复来看,窄依赖恢复起来更高效,因为他只需要找到父RDD的一个对应分区即可,而且可以在不同节点上并行计算做恢复。宽依赖则牵扯到父RDD的多个分区,恢复起来相对复杂一些。
  3. 在这里我们引入一个新的概念,Stage。Stage可以简单的理解为是由一组RDD组成的可进行优化的执行计划。若RDD的衍生关系为窄依赖,则可以放在同一个stage中运行。若RDD的依赖关系为宽依赖,则要划分到不同的Stage中。这样Spark在执行任务时,会按照Stage的划分,生成一个完整的最优的执行计划。

划分以来的依据

划分宽依赖和窄依赖的关键点在:分区的依赖关系

也就是说父RDD的一个分区的数据,是给子RDD的一个分区,还是给子RDD的所有分区。

父RDD的每一个分区,是被一个子RDD的一个分区依赖(一对一),就是窄依赖。

如果父RDD的每一个分区,是被子RDD的各个分区所依赖(一对多),就是宽依赖。

一旦有宽依赖发生,就意味着会发生数据的shuffle。发生了shuffle也就意味着生成了新的stage。

窄依赖

窄依赖是指父RDD的一个分区,被子RDD的一个分区所依赖(一对一)

输入输出一对一的算子,且结果RDD的分区结构不变,主要是map/flatMap/filter

输入输出一对一的算子但结果RDD的分区结构发生了变化,如union/coalesce

Spark入门篇——RDD的血缘

宽依赖

宽依赖指的是父RDD的一个分区,被子RDD的所有个分区依赖(一对多)。 

对单个RDD基于key进行重组和归并,如groupByKey,reduceByKey等。

对于两个RDD基于key进行join和重组。如join(父RDD不是hash-Partitioned)

需要进行分区,如partitionBy

Spark入门篇——RDD的血缘

 join的依赖

宽依赖

Spark入门篇——RDD的血缘

 窄依赖

Spark入门篇——RDD的血缘

 依赖与Stage的划分

Spark将窄依赖划分到同一个Stage中,因为他们可以进行流水线计算。而宽依赖往往意味着shuffle操作,这也是Spark划分stage的主要边界。一个Stage的开始就是从外部存储或者shuffle结果中读取数据;一个Stage的结束就是发生shuffle或者生成结果时。只要发生了shuffle,就会有Stage的划分。

Stage的类别

一个Job是由action算子触发的,一个Job中包含一个或者多个Stage,其中分为ResultStage(一个)和ShuffleStage(多个)。

Spark入门篇——RDD的血缘

 依赖与容错

宽、窄依赖的概念不知用于在stage的划分,对容错特很有用,RDD的Lineage会记录RDD的元数据信息和转换行为,当该RDD的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区。

dependency代表了RDD之间的依赖关系,即血缘(Lineage)。

数据进行切片后,每个分区对应的数据,是固定的。(其实偏移量和结束的偏移量)

分区是在driver端生成的,即使某一个分区挂掉了也没关系,driver端会根据rdd的依赖关系,重启新的task进行任务执行。

转换算子中间发生失败

  1. 运算是窄依赖:只要把丢失的父RDD分区重新计算即可,跟其他节点没有依赖,这样可以大大减少恢复丢失的数据分区的开销。
  2. 运算是宽依赖:需要父RDD的所有分区都存在,重算代价较高。
  3. 如果整个节点挂掉,driver会把任务在其他的节点中的executor中重启。
  4. 缓存或者增加检查点:当Lineage特别长或者有宽依赖的时候,主动调用checkpoint把当前数据写入稳定存储,作为检查点。但checkpoint会产生磁盘开销。因为做checkpoint时需要重新计算该RDD的内容,所以该RDD最好已经在内存中缓存了

为了提高运算的效率,更好的解决容错问题,spark提供了一系列的解决方案,缓存,检查点(checkpoint)

缓存:把rdd的数据,写入到内存或者磁盘

checkpoint: 写入到hdfs中

使用缓存和持久化来提升运行效率

DAG的生成

DAG叫做有向无环图,指任意一条边有方向,且不存在环路的图。其中点代表RDD,边代表RDD之间的依赖关系。

在Spark中每一个操作生成一个RDD,RDD之间连一条边,最后这些RDD和它们之间的边组成一个有向无环图,这个就是Spark的DAG图。原始RDD通过一系列的转换就形成了DAG,根据RDD之间的依赖关系的不同将DAG划分为不同的Stage,对于窄依赖,分区的转换处理在Stage中完成计算。对于宽依赖,由于shuffle的存在,只能在parentRDD处理完成之后,才能开始接下来的计算,因此宽依赖是划分Stage的依据

Spark入门篇——RDD的血缘

总结

DAG是有向无环图,代表着RDD的转换过程,其实就是代表着数据的流向。

DAG是有边界的,有开始,有结束。通过sparkContext创建RDD就是开始,触发action就会生成一个完整的DAG。DAG会被切分为多个Stage(阶段),切分的依据就是宽依赖(Shuffle),会先提交前面的stage,然后提交后面的stage,一个stage中根据最后一个RDD的分区出划分出多个Task,多个Task可以并行执行。