Spark Stage 的划分

博客内容介绍了Spark中Stage的划分规则,特别是窄依赖和宽依赖如何影响Stage的生成。窄依赖如map、filter和union会导致不产生新Stage,而宽依赖如join操作会形成新的Stage并涉及Shuffle过程。Stage之间的依赖关系通过parents字段记录,已存在的Stage会被重新使用。
摘要由CSDN通过智能技术生成
     想了解Shuffle 的处理流程,首先要了解Spark是如何划分Stage的。下面,让我们看看 Spark 是如何根据RDD 的依赖关系来划分Stage。
     首先 我们必须要理解 Spark 中RDD的依赖关系.
     
     1.Rdd的依赖关系:
          Rdd的依赖有两种:
               1.宽依赖( Wide Dependency)
               2.窄依赖( Narrow Dependency)
          以下图说明RDD的窄依赖和宽依赖
        
窄依赖
窄依赖指父RDD的每一个分区最多被一个子RDD的分区所用,表现为
  • 一个父RDD的分区对应于一个子RDD的分区
  • 两个父RDD的分区对应于一个子RDD 的分区。

如上面的map,filter,union属于第一类窄依赖,而join with inputs co-partitioned(对输入进行协同划分的join操作)则为第二类窄依赖


如果有多个父RDD的分区对应于同一个子RDD的分区不能称之为窄依赖?

宽窄依赖与容错性
Spark基于lineage的容错性是值,如果一个RDD出错,那么可以从它的所有父RDD重新计算所得,如果一个RDD仅有一个父RDD(即窄依赖),那么这种重新计算的代价会非常小。
Spark基于Checkpoint(物化)的容错机制何解?在上图中,宽依赖得到的结果(经历过Shuffle过程)是很昂贵的,因此,Spark将此结果物化到磁盘上了,以备后面使用

宽依赖
宽依赖指子RDD的每个分区都要依赖于父RDD的所有分区,这是shuffle类操作,上图中的groupByKey和对输入未协同划分的join操作就是宽依赖。

窄依赖细说
窄依赖对优化很有利。逻辑上,每个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。


Spark中关于Dependency的源代码

/**
 * :: DeveloperApi ::
 * Base class for dependencies.
 */
@DeveloperApi
abstract class Dependency[ Textends Serializable {
  def  rdd: RDD[ T]
}


/**
 * :: DeveloperApi ::
 * Base class for dependencies where each partition of the child RDD depends on a small number
 * of partitions of the parent RDD. Narrow dependencies allow for pipelined execution.
  * 这里是说,窄依赖是指子RDD的每个Partition只依赖于父RDD很少部分的的分区,文档明显说的不对!窄依赖起码需要,父RDD的每个Partition只被一个子RDD的Partition依赖
 */
@DeveloperApi
abstract class NarrowDependency[ T](_rdd: RDD[ T])  extends Dependency[ T] {
  /**
   * Get the parent partitions for a child partition.
   *  @param partitionId  a partition of the child RDD
   *  @return  the partitions of the parent RDD that the child partition depends upon
   */
  def  getParents(partitionId:  Int):  Seq[ Int]

  override def  rdd: RDD[ T] = _rdd
}

/**
  * ShuffleDependency指的是,子RDD的partition部分依赖于父RDD的每个Partition 部分依赖被称为 ShuffleDependency。
  * 其实 ShuffleDependency 跟 MapReduce 中 shuffle 的数据依赖相同
  * (mapper 将其 output 进行 partition,然后每个 reducer 会将所有 mapper 输出中属于自己的 partition 通过 HTTP fetch 得到)。
  */
/**
 * :: DeveloperApi ::
 * Represents a dependency on the output of a shuffle stage. Note that in the case of shuffle,
 * the RDD is transient since we don't need it on the executor side.
 *
 *  @param _rdd  the parent RDD
 *  @param partitioner  partitioner used to partition the shuffle output
 *  @param serializer  [[ org.apache.spark.serializer.Serializer Serializer ]]  to use. If set to None,
 *                   the default serializer, as specified by  ` spark.serializer config option, will
 *                   be used.
 *  @param keyOrdering  key ordering for RDD's shuffles
 *  @param aggregator  map/reduce-side aggregator for RDD's shuffle
 *  @param mapSideCombine  whether to perform partial aggregation (also known as map-side combine)
 */
@DeveloperApi
class ShuffleDependency[ K: ClassTag V: ClassTag C: ClassTag](
    @transient  private val _rdd: RDD[_ <: Product2[ K V]] ,
    val <
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值