我们知道Spark中的RDD可以从本地集合、外部文件系统创建,从其它RDD转化得到。从其它RDD通过转换算子得到新的RDD,这两个RDD之间具有依赖关系,即血缘。RDD和它依赖的父RDD之间有两种不同的依赖类型,即宽依赖和窄依赖。下面我们具体介绍一下Spark中的血缘关系。
一、血缘关系
RDD只支持粗粒度转换,即在大量记录上执行的单个操作。将创建RDD的一系列Lineage(血统)记录下来,以便恢复丢失的分区。RDD的Lineage会记录RDD的元数据信息和转换行为,当该RDD的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区。
代码实现:
object Lineage01 {
def main(args: Array[String]): Unit = {
//1.创建SparkConf并设置App名称
val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")
//2.创建SparkContext,该对象是提交Spark App的入口
val sc: SparkContext = new SparkContext(conf)
//3.加载数据进行并进行数据转换
val rdd: RDD[String] = sc.makeRDD(List("hello world", "hello spark"))
val wordRDD: RDD[String] = rdd.flatMap(_.split(" "))
val mapRDD: RDD[(String, Int)] = wordRDD.map((_,1))
val resultRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_+_)
//4.打印RDD之间的血缘关系
println(resultRDD.toDebugString)
//5.触发计算
resultRDD.collect()
//6.关闭连接
sc.stop()
}
}
查看运行结果:
结果说明:圆括号中的数字12表示RDD的并行度,也就是有几个分区,[0]是第一步操作,创建集合RDD,[1]是第二步操作,执行flatMap算子,[2]是第三步操作,执行map算子,[3]是第四步操作,执行reduceByKey算子,因reduceByKey算子存在Shuffle过程,所以结果前面缩进显示,代表新的stage划分。血缘关系将RDD的一系列操作记录下来,当该RDD的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区。
二、依赖关系
理解RDD是如何工作的,最重要的就是理解Transformations。RDD之间的关系可以从以下角度来理解,RDD是从哪些RDD转换而来,RDD依赖于父RDD的哪些Partition,这种关系就是RDD之间的依赖。
代码实现:
object Lineage02 {
def main(args: Array[String]): Unit = {
//1.创建SparkConf并设置App名称
val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")
//2.创建SparkContext,该对象是提交Spark App的入口
val sc: SparkContext = new SparkContext(conf)
//3.打印RDD之间的依赖关系
val rdd: RDD[String] = sc.makeRDD(List("hello world","hello spark"))
println(rdd.dependencies)
println("----------------------")
val wordRDD: RDD[String] = rdd.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)
//4.触发计算
resultRDD.collect()
//5.查看localhost:4040页面,观察DAG图
Thread.sleep(1000000)
//6.关闭连接
sc.stop()
}
}
查看运行结果:
结果说明:第一步从集合中创建RDD,不是从其它RDD转换而来,没有RDD之间的依赖关系,所以执行sc.makeRDD打印依赖关系结果为List(),而后的flatMap算子和map算子对RDD进行数据转换操作,每一个父RDD的分区最多被子RDD的一个分区使用,所以依赖结果是OneToOneDependency,而reduceByKey,同一个父RDD的分区被多个子RDD的分区依赖,是一对多的关系,会引起Shuffle,所以依赖结果是ShuffleDependency。
查看DAG图:
注意:RDD和它依赖的父RDD的依赖关系有两种不同的类型,即窄依赖(NarrowDependency)和宽依赖(ShuffleDependency)。
窄依赖:窄依赖表示每一个父RDD的Partition最多被子RDD的一个Partition使用,是一对一或者多对一的关系。如下图所示:
宽依赖:宽依赖表示同一个父RDD的Partition被多个子RDD的Partition依赖,是一对多的关系,会引起Shuffle。如下图所示:
三、总结
通过这篇文章,大家对Spark RDD的血缘关系是不是有进一步的了解了,RDD的血缘关系是Spark的容错机制之一,当某个RDD的部分分区数据丢失时,它可以根据血缘关系来重新运算,以恢复丢失的数据分区。