Spark(10):RDD依赖关系

文章详细介绍了Spark中的ResilientDistributedDatasets(RDD)的核心概念,包括RDD的血缘关系(Lineage),如何通过血缘关系恢复丢失数据;RDD之间的依赖关系,区分了窄依赖(每个父RDD分区对应一个子RDD分区)和宽依赖(触发Shuffle);以及RDD的阶段划分和任务划分机制,阐述了DAG在作业执行中的作用。此外,还展示了如何通过代码示例查看RDD的依赖和分区信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

0. 相关文章链接

1. RDD 血缘关系

2. RDD依赖关系

3. RDD窄依赖

6. RDD宽依赖

7. RDD阶段划分

8. RDD阶段划分源码

9. RDD任务划分


0. 相关文章链接

 Spark文章汇总 

1. RDD 血缘关系

        RDD 只支持粗粒度转换,即在大量记录上执行的单个操作。将创建 RDD 的一系列 Lineage(血统)记录下来,以便恢复丢失的分区。RDD 的 Lineage 会记录 RDD 的元数据信息和转换行为,当该 RDD 的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区。 

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @ date: 2023/7/4
 * @ author: yangshibiao
 * @ desc: 项目描述
 */
object ModelTest {

    def main(args: Array[String]): Unit = {

        val conf: SparkConf = new SparkConf()
            .setAppName("ModelTest")
            .setMaster("local[*]")

        val sc: SparkContext = new SparkContext(conf)

        val fileRDD: RDD[String] = sc.textFile("input/1.txt")
        println(fileRDD.toDebugString)
        println("----------------------")
        val wordRDD: RDD[String] = fileRDD.flatMap(_.split(" "))
        println(wordRDD.toDebugString)
        println("----------------------")

        val mapRDD: RDD[(String, Int)] = wordRDD.map((_,1))
        println(mapRDD.toDebugString)
        println("----------------------")

        val resultRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_+_)
        println(resultRDD.toDebugString)

        resultRDD.collect()

    }
}

2. RDD依赖关系

这里所谓的依赖关系,其实就是两个相邻 RDD 之间的关系


import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @ date: 2023/7/4
 * @ author: yangshibiao
 * @ desc: 项目描述
 */
object ModelTest {

    def main(args: Array[String]): Unit = {

        val conf: SparkConf = new SparkConf()
            .setAppName("ModelTest")
            .setMaster("local[*]")

        val sc: SparkContext = new SparkContext(conf)

        val fileRDD: RDD[String] = sc.textFile("input/1.txt")
        println(fileRDD.dependencies)
        println("----------------------")

        val wordRDD: RDD[String] = fileRDD.flatMap(_.split(" "))
        println(wordRDD.dependencies)
        println("----------------------")

        val mapRDD: RDD[(String, Int)] = wordRDD.map((_,1))
        println(mapRDD.dependencies)
        println("----------------------")

        val resultRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_+_)
        println(resultRDD.dependencies)

        resultRDD.collect()

    }
}

3. RDD窄依赖

窄依赖表示每一个父(上游)RDD 的 Partition 最多被子(下游)RDD 的一个 Partition 使用,窄依赖我们形象的比喻为独生子女。 

class OneToOneDependency[T](rdd: RDD[T]) extends NarrowDependency[T](rdd)  

6. RDD宽依赖

宽依赖表示同一个父(上游)RDD 的 Partition 被多个子(下游)RDD 的 Partition 依赖,会引起 Shuffle,总结:宽依赖我们形象的比喻为多生。 

class ShuffleDependency[K: ClassTag, V: ClassTag, C: ClassTag](
    @transient private val _rdd: RDD[_ <: Product2[K, V]],
    val partitioner: Partitioner, 
    val serializer: Serializer = SparkEnv.get.serializer,     
    val keyOrdering: Option[Ordering[K]] = None,     
    val aggregator: Option[Aggregator[K, V, C]] = None,     
    val mapSideCombine: Boolean = false
)   extends Dependency[Product2[K, V]]  

7. RDD阶段划分

DAG(Directed Acyclic Graph)有向无环图是由点和线组成的拓扑图形,该图形具有方向,不会闭环。例如,DAG 记录了 RDD 的转换过程和任务的阶段。 

8. RDD阶段划分源码

try { 
  // New stage creation may throw an exception if, for example, jobs are run on a 
  // HadoopRDD whose underlying HDFS files have been deleted. 
  finalStage = createResultStage(finalRDD, func, partitions, jobId, callSite)
} catch {   
    case e: Exception => 
        logWarning("Creating new stage failed due to exception - job: " + jobId, e)     listener.jobFailed(e)     return 
} 
 
…… 
private def createResultStage(   
    rdd: RDD[_],   
    func: (TaskContext, Iterator[_]) => _,   
    partitions: Array[Int],   
    jobId: Int,   
    callSite: CallSite
): ResultStage = { 
    val parents = getOrCreateParentStages(rdd, jobId) 
    val id = nextStageId.getAndIncrement() 
    val stage = new ResultStage(id, rdd, func, partitions, parents, jobId, callSite) 
    stageIdToStage(id) = stage 
    updateJobIdStageIdMaps(jobId, stage) 
    stage 
} 
 
…… 
 
private def getOrCreateParentStages(rdd: RDD[_], firstJobId: Int): List[Stage] = { 
    getShuffleDependencies(rdd).map { 
        shuffleDep =>   getOrCreateShuffleMapStage(shuffleDep, firstJobId) 
    }.toList 
} 
 
…… 
 private[scheduler] def getShuffleDependencies(   
    rdd: RDD[_]
): HashSet[ShuffleDependency[_, _, _]] = { 
    val parents = new HashSet[ShuffleDependency[_, _, _]] 
    val visited = new HashSet[RDD[_]] 
    val waitingForVisit = new Stack[RDD[_]] 
    waitingForVisit.push(rdd) 
    while (waitingForVisit.nonEmpty) {   
        val toVisit = waitingForVisit.pop()   
        if (!visited(toVisit)) {     
            visited += toVisit 
            toVisit.dependencies.foreach {       
                case shuffleDep: ShuffleDependency[_, _, _] => parents += shuffleDep
                case dependency => waitingForVisit.push(dependency.rdd) 
            } 
        } 
    } 
    parents 
} 

9. RDD任务划分

RDD 任务切分中间分为:Application、Job、Stage 和 Task 

  • Application:初始化一个 SparkContext 即生成一个 Application; 
  • Job:一个 Action 算子就会生成一个 Job; 
  • Stage:Stage 等于宽依赖(ShuffleDependency)的个数加 1; 
  • Task:一个 Stage 阶段中,最后一个 RDD 的分区个数就是 Task 的个数。 

注意:Application->Job->Stage->Task 每一层都是 1 对 n 的关系。  


注:其他Spark相关系列文章链接由此进 ->  Spark文章汇总 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

电光闪烁

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

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

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

打赏作者

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

抵扣说明:

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

余额充值