RDD(弹性分布式数据集)血缘关系是Spark容错机制的核心组成部分,记录了RDD之间的转换依赖路径。

血缘关系的基本概念

RDD血缘关系(Lineage)是指通过RDD转换操作形成的依赖关系图,记录了RDD如何从数据源或父RDD演变而来的完整过程。由于RDD本身不保存数据,每个RDD都会保存自己的血缘关系,当某个转换过程出现数据丢失时,可以根据血缘关系重新从数据源开始计算恢复。

血缘关系的形成机制

血缘关系的形成基于RDD的粗粒度转换特性,即在大量记录上执行的单个操作。当我们计算一个RDD时,会依赖一个或多个父RDD的数据,而这些父RDD又会依赖自身的父RDD,最终形成一个有向无环图(DAG)。例如,在文本处理的转换链中:textFile → flatMap → map → reduceByKey,每个RDD都记录了其创建逻辑和父RDD信息。

依赖关系的类型划分

RDD依赖关系分为两种主要类型:窄依赖宽依赖。窄依赖表现为OneToOneDependency,即父RDD的每个分区最多被子RDD的一个分区使用。宽依赖则涉及Shuffle操作,父RDD的分区可能被子RDD的多个分区依赖。

血缘关系的功能价值

血缘关系的主要价值在于容错恢复和计算优化。当RDD的某个分区数据丢失时,Spark可以通过追溯血缘关系获取足够信息来重新运算和恢复丢失的数据分区。窄依赖的恢复更为高效,只需重新计算对应的父分区,而宽依赖可能涉及多个父分区的重新计算。

血缘关系的实践应用

开发者可以通过toDebugString方法查看RDD的血缘关系,使用dependencies方法查看依赖关系。这种设计使得具有血缘关系的操作能够实现管道化处理,避免了多次转换操作间的数据同步等待

例子:

public class Test01_Dep {
    public static void main(String[] args) {

        // 1. 创建配置对象
        SparkConf conf = new SparkConf().setMaster("local[*]").setAppName("WordCount");
        // 2. 创建sparkContext
        JavaSparkContext sc = new JavaSparkContext(conf);
        // 3. 编写代码
        JavaRDD<String> stringJavaRDD = sc.textFile("input/2.txt");
        System.out.println(stringJavaRDD.toDebugString());
        System.out.println("---------------------------");

        JavaRDD<String> stringJavaRDD1 = stringJavaRDD.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterator<String> call(String s) throws Exception {
                String[] split = s.split(" ");
                List<String> list = Arrays.asList(split);
                return list.iterator();
            }
        });
        System.out.println(stringJavaRDD1.toDebugString());
        System.out.println("---------------------------");

        JavaPairRDD<String, Integer> javaPairRDD = stringJavaRDD1.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<>(s, 1);
            }
        });
        System.out.println(javaPairRDD.toDebugString());
        System.out.println("--------------------------");

        JavaPairRDD<String, Integer> javaPairRDD1 = javaPairRDD.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer integer, Integer integer2) throws Exception {
                return integer + integer2;
            }
        });
        System.out.println(javaPairRDD1.toDebugString());
        System.out.println("--------------------------");


        sc.stop();

    }
}

执行结果

(2) input/2.txt MapPartitionsRDD[1] at textFile at Test01_Dep.java:24 []
 |  input/2.txt HadoopRDD[0] at textFile at Test01_Dep.java:24 []
---------------------------
(2) MapPartitionsRDD[2] at flatMap at Test01_Dep.java:28 []
 |  input/2.txt MapPartitionsRDD[1] at textFile at Test01_Dep.java:24 []
 |  input/2.txt HadoopRDD[0] at textFile at Test01_Dep.java:24 []
---------------------------
(2) MapPartitionsRDD[3] at mapToPair at Test01_Dep.java:39 []
 |  MapPartitionsRDD[2] at flatMap at Test01_Dep.java:28 []
 |  input/2.txt MapPartitionsRDD[1] at textFile at Test01_Dep.java:24 []
 |  input/2.txt HadoopRDD[0] at textFile at Test01_Dep.java:24 []
--------------------------
(2) ShuffledRDD[4] at reduceByKey at Test01_Dep.java:48 []
 +-(2) MapPartitionsRDD[3] at mapToPair at Test01_Dep.java:39 []
    |  MapPartitionsRDD[2] at flatMap at Test01_Dep.java:28 []
    |  input/2.txt MapPartitionsRDD[1] at textFile at Test01_Dep.java:24 []
    |  input/2.txt HadoopRDD[0] at textFile at Test01_Dep.java:24 []
--------------------------

:(*)中内容为分区数

更多推荐