【Spark篇】--Spark中的宽窄依赖和Stage的划分

一、前述

RDD之间有一系列的依赖关系,依赖关系又分为窄依赖和宽依赖。

Spark中的Stage其实就是一组并行的任务,任务是一个个的task 。

二、具体细节

 

  • 窄依赖

RDD和子RDD partition之间的关系是一对一的。或者父RDD一个partition只对应一个子RDD的partition情况下的父RDD和子RDD partition关系是多对一的。不会有shuffle的产生。父RDD一个分区去到子RDD的一个分区

 

  • 宽依赖

RDD与子RDD partition之间的关系是一对多。会有shuffle的产生。父RDD的一个分区的数据去到子RDD的不同分区里面。

其实区分宽窄依赖主要就是看父RDD的一个Partition的流向,要是流向一个的话就是窄依赖,流向多个的话就是宽依赖。看图理解:

 

  • Stage概念

Spark任务会根据RDD之间的依赖关系,形成一个DAG有向无环图,DAG会提交给DAGScheduler,DAGScheduler会把DAG划分相互依赖的多个stage,划分stage的依据就是RDD之间的宽窄依赖。遇到宽依赖就划分stage,每个stage包含一个或多个task任务。然后将这些task以taskSet的形式提交给TaskScheduler运行。     stage是由一组并行的task组成。

 

  • stage切割规则

 切割规则:从后往前遇到宽依赖就切割stage。

 

 

 

  • stage计算模式

    pipeline管道计算模式,pipeline只是一种计算思想,模式。

 

备注:图中几个理解点:

   1、Spark的pipeLine的计算模式,相当于执行了一个高阶函数f3(f2(f1(textFile))) !+!+!=3 也就是来一条数据然后计算一条数据,把所有的逻辑走完,然后落地,准确的说一个task处理遗传分区的数据 因为跨过了不同的逻辑的分区。而MapReduce是 1+1=2,2+1=3的模式,也就是计算完落地,然后在计算,然后再落地到磁盘或内存,最后数据是落在计算节点上,按reduce的hash分区落地。所以这也是比Mapreduce快的原因,完全基于内存计算。

   2、管道中的数据何时落地:shuffle write的时候,对RDD进行持久化的时候。

   3.   Stage的task并行度是由stage的最后一个RDD的分区数来决定的 。一般来说,一个partiotion对应一个task,但最后reduce的时候可以手动改变reduce的个数,也就是分区数,即改变了并行度。例如reduceByKey(XXX,3),GroupByKey(4),union由的分区数由前面的相加。

   4.、如何提高stage的并行度:reduceBykey(xxx,numpartiotion),join(xxx,numpartiotion)

 

  • 测试验证pipeline计算模式
    import org.apache.spark.SparkConf
    import org.apache.spark.SparkContext
    import java.util.Arrays
    
    object PipelineTest {
      def main(args: Array[String]): Unit = {
        val conf = new SparkConf()
        conf.setMaster("local").setAppName("pipeline");
        val sc = new SparkContext(conf)
        val rdd = sc.parallelize(Array(1,2,3,4))
        val rdd1 = rdd.map { x => {
          println("map--------"+x)
          x
        }}
        val rdd2 = rdd1.filter { x => {
          println("fliter********"+x)
          true
        } }
        rdd2.collect()
        sc.stop()
      }
    }

     

    可见是按照所有的逻辑将数据一条条的执行。!!!

     

 

 

 

### Spark依赖窄依赖的概念及区别 #### 窄依赖 (Narrow Dependency) 窄依赖是指子 RDD 的每个分区最多依赖于父 RDD 的一个分区。这意味着数据可以在不需要 shuffle 的情况下直接从父 RDD 转移到子 RDD。由于不存在跨分区的数据交换,因此可以高效地进行流水线操作[^1]。 例如,在 `map` 或 `filter` 操作中,输入数据的每一个分区只会映射到输出的一个分区,不会引发大规模的数据重分布。这种特性使得窄依赖下的任务能够快速完成,因为它们只需要顺序读取本地磁盘上的数据即可[^2]。 ```python rdd = sc.parallelize([1, 2, 3]) mapped_rdd = rdd.map(lambda x: x * 2) # 这是一个典型的窄依赖例子 ``` --- #### 依赖 (Wide Dependency) 依赖则是指子 RDD 的某个分区可能会依赖于父 RDD 的多个分区甚至全部分区。这种情况通常发生在需要对数据进行重新分区或者聚合的操作时,比如 `groupByKey` `reduceByKey`。为了实现这样的转换,必须通过 shuffle 将来自不同节点的数据集中起来再分配给新的分区[^3]。 在依赖的情况下,Spark 需要将整个 DAG 划分为不同的 Stage 来管理复杂的计算流程。每当遇到一次 shuffle 操作时,就会创建一个新的 Stage 边界[^4]。 ```python rdd = sc.parallelize([(1, 'a'), (2, 'b'), (1, 'c')]) reduced_rdd = rdd.reduceByKey(lambda a, b: a + b) # 属于依赖的例子 ``` --- #### 主要区别总结如下表所示: | 特性 | 窄依赖 | 依赖 | |-----------------|---------------------------------|--------------------------------| | 数据流动模式 | 不涉及 Shuffle | 必须经过 Shuffle | | 分区依赖 | 子分区仅依赖单个父分区 | 子分区可依赖多个父分区 | | Fault Tolerance | 可以只重算丢失的那个分区链条 | 需要回溯至共同祖先并重做更多工作 | | 性能影响 | 更加轻量级 | 开销较大 | --- ### 实际应用中的意义 了解这两种依赖类型有助于优化 Spark 应用程序性能。尽量减少不必要的 shuffles 是提高效率的关键之一。可以通过调整逻辑设计来避免过多使用那些容易引起依赖的操作符(如 join),转而寻找替代方案降低整体复杂度[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

L先生AI课堂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值