RDD 依赖关系
1、血缘关系&依赖关系
RDD只支持粗粒度转换,即在大量记录上执行的单个操作。将创建RDD的一系列Lineage(血统)记录下来,以便恢复丢失的分区。RDD的Lineage会记录RDD的元数据信息和转换行为,当该RDD的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区。
血缘关系
依赖关系
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | object Spark 01 _ lineage { def main(args : Array[String]) : Unit = { //创建 配置对象 val conf : SparkConf = new SparkConf().setAppName( "wordcount" ).setMaster( "local[*]" ) //配置上下文对象 var sc : SparkContext = new SparkContext(conf) val rdd : RDD[String] = sc.makeRDD(List( "hello spark" , "flink" , "hello" , "word hello" )) //rdd 血缘关系 println(rdd.toDebugString) println(rdd.dependencies) println( "**************************************" ) val flatMap : RDD[String] = rdd.flatMap( _ .split( " " )) println(flatMap.toDebugString) println(flatMap.dependencies) println( "**************************************" ) val map : RDD[(String, Int)] = flatMap.map(( _ , 1 )) println(map.toDebugString) println(map.dependencies) println( "**************************************" ) val resRDD : RDD[(String, Int)] = map.reduceByKey( _ + _ ) println(resRDD.toDebugString) println(resRDD.dependencies) resRDD.collect().foreach(println) //关闭 sc.stop() } } |
血缘关系
(4) ParallelCollectionRDD[0] at makeRDD at Spark01_lineage.scala:15 [] ************************************** (4) MapPartitionsRDD[1] at flatMap at Spark01_lineage.scala:20 [] | ParallelCollectionRDD[0] at makeRDD at Spark01_lineage.scala:15 [] ************************************** (4) MapPartitionsRDD[2] at map at Spark01_lineage.scala:24 [] | MapPartitionsRDD[1] at flatMap at Spark01_lineage.scala:20 [] | ParallelCollectionRDD[0] at makeRDD at Spark01_lineage.scala:15 [] ************************************** (4) ShuffledRDD[3] at reduceByKey at Spark01_lineage.scala:28 [] +-(4) MapPartitionsRDD[2] at map at Spark01_lineage.scala:24 [] | MapPartitionsRDD[1] at flatMap at Spark01_lineage.scala:20 [] | ParallelCollectionRDD[0] at makeRDD at Spark01_lineage.scala:15 []
血缘关系说明
(4) ParallelCollectionRDD[0] at makeRDD at Spark01_lineage.scala:15 [] //第15行通过集合创建RDD,makeRDD 是 ParallelCollectionRDD 的封装 ************************************** (4) MapPartitionsRDD[1] at flatMap at Spark01_lineage.scala:20 [] //第20行通过flatMap创建RDD,flatMap 是 MapPartitionsRDD的封装 | ParallelCollectionRDD[0] at makeRDD at Spark01_lineage.scala:15 [] ************************************** (4) MapPartitionsRDD[2] at map at Spark01_lineage.scala:24 [] //第24行map转换结构,map 也是 MapPartitionsRDD的封装 | MapPartitionsRDD[1] at flatMap at Spark01_lineage.scala:20 [] | ParallelCollectionRDD[0] at makeRDD at Spark01_lineage.scala:15 [] ************************************** (4) ShuffledRDD[3] at reduceByKey at Spark01_lineage.scala:28 [] //28行执行了 reduceByKey ,存在shuffle,单独列了一行 +-(4) MapPartitionsRDD[2] at map at Spark01_lineage.scala:24 [] | MapPartitionsRDD[1] at flatMap at Spark01_lineage.scala:20 [] | ParallelCollectionRDD[0] at makeRDD at Spark01_lineage.scala:15 []
整个RDD的血缘关系是从下至上的,输出的依赖关系
List() List(org.apache.spark.OneToOneDependency@27d57a2c) List(org.apache.spark.OneToOneDependency@59532566) List(org.apache.spark.ShuffleDependency@5524b72f)
全局搜索(ctrl+n)org.apache.spark.OneToOneDependency
1 2 3 4 | @ DeveloperApi class OneToOneDependency[T](rdd : RDD[T]) extends NarrowDependency[T](rdd) { override def getParents(partitionId : Int) : List[Int] = List(partitionId) } |
NarrowDependency 即窄依赖,查看当前类名是
1 2 3 4 | @ DeveloperApi abstract class Dependency[T] extends Serializable { def rdd : RDD[T] } |
按 ctrl + h 查看实现方法
注意:要想理解RDD是如何工作的,最重要的就是理解Transformations。
RDD 之间的关系可以从两个维度来理解: 一个是 RDD 是从哪些 RDD 转换而来, 也就是 RDD 的 parent RDD(s)是什么; 另一个就是 RDD 依赖于 parent RDD(s)的哪些 Partition(s). 这种关系就是 RDD 之间的依赖.
RDD和它依赖的父RDD(s)的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency)
2、宽/窄依赖
窄依赖 窄依赖表示每一个父RDD的Partition最多被子RDD的一个Partition使用,窄依赖我们形象的比喻为独生子女。
宽依赖 宽依赖表示同一个父RDD的Partition被多个子RDD的Partition依赖,会引起Shuffle,总结:宽依赖我们形象的比喻为超生。
具有宽依赖的 transformations 包括: sort, reduceByKey, groupByKey, join, 和调用rePartition函数的任何操作.宽依赖对 Spark 去评估一个 transformations 有更加重要的影响, 比如对性能的影响.
3、Spark 中Job 调度
一个Spark应用包含一个驱动进程(driver process,在这个进程中写Spark的逻辑代码)和多个执行器进程(executor process,跨越集群中的多个节点)。Spark 程序自己是运行在驱动节点, 然后发送指令到执行器节点。一个Spark集群可以同时运行多个Spark应用, 这些应用是由集群管理器(cluster manager)来调度。Spark应用可以并发的运行多个job, job对应着给定的应用内的在RDD上的每个 action操作。Spark应用:一个Spark应用可以包含多个Spark job, Spark job是在驱动程序中由SparkContext 来定义的。当启动一个 SparkContext 的时候, 就开启了一个 Spark 应用。 一个驱动程序被启动了, 多个执行器在集群中的多个工作节点(worker nodes)也被启动了。 一个执行器就是一个 JVM, 一个执行器不能跨越多个节点, 但是一个节点可以包括多个执行器。一个 RDD 会跨多个执行器被并行计算. 每个执行器可以有这个 RDD 的多个分区, 但是一个分区不能跨越多个执行器.
Spark Job 的划分:由于Spark的懒执行, 在驱动程序调用一个action之前, Spark 应用不会做任何事情,针对每个action,Spark 调度器就创建一个执行图(execution graph)和启动一个 Spark job。每个 job 由多个stages 组成, 这些 stages 就是实现最终的 RDD 所需的数据转换的步骤。一个宽依赖划分一个stage。每个 stage 由多个 tasks 来组成, 这些 tasks 就表示每个并行计算, 并且会在多个执行器上执行。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下