RDD设计背景
- RDD被设计用来减少IO出现的,提供了一中抽象的数据结构,不用担心的底层数据的分布式特性。只需将具体的应用逻辑将一些列转换进行处理。不同的RDD之间的转换操作形成依实现管道话。从而避免中间结果落地的存储。降低数据复制,磁盘IO和序列化开销。
- RDD是一个不可变的分布式对象集合。每个RDD会被分成多个分区,这些分区运行在集群的不同节点上。每个分区就是一个数据集片段。RDD提供的是一种高度受限的共享内存模型,既RDD是只读的记录分区的集合,不能直接修改,只能给予文档sing的物理存储中的数据来创建RDD,或者是从其他RDD操作上执行转换操作得到新的RDD。
- RDD提供了一组丰富的操作,并且支持常见的数据运算,包含两种,分别为‘’行动‘’和‘’转换‘’两种。行动 是用来执行计算并制定输出的形式。后者指定RDD之间的相互依赖关系。两类的操作区别是转换是用来转换RDD得到新的RDD,行动操作是接收RDD但是返回的就不是RDD了,是值或者其他集合等内容。一般我们都会把行动操作的结果存入到数据存储结构中,如数据库hbase.等
- RDD的操作流程。一般如下。
- 通过RDD读取外部数据库或者数据源进行创建。
- RDD经过一些列操作进行转换(map,filter),形成新的RDD,提供给下一个RDD进行转换使用。
- 最后经过行动操作,进行处理输入到外部的数据源中或者成为集合内容。
- RDD在操作中是属于惰性调用,只有到达‘’行动‘’这个操作之后,才会开始进行真正的计算。
SparkConf conf = new SparkConf().setMaster("local").setAppName("My App Test")
JavaRDD<String> pairRDD = scContext.parallelize(Arrays.asList("a","b","c"))
pairRDD.foreach(result -> System.out.println(result.toString()))
![从数据读入到行动结束开始运算](https://i-blog.csdnimg.cn/blog_migrate/e959e78df2f000acc91fb02ed35ac0a8.webp?x-image-process=image/format,png)
RDD特性
- 为什么RDD能实现高效计算?
- 高效的容错性。 分布式共享内存。键值存储、内存数据库等。为了实现容错必须在集群节点进行数据的复制,或者记录日志。简单的说,在这些节点之间会发生大量的数据传输,对于数据密集型应用而言会带来很大的开销。但是由于RDD在设计中数据至刻度,不可更改,这就造成我们必须进行RDD的转换,将父RDD转换成子RDD。这样从开始到结束创建的RDD就会形成一幅血缘图.在这些转换的过程中我们会把中间结果持久化到内存,数据再内从中的多个RDD之间进行传递,不需要落地到磁盘上,但是内润空间不足 的时候,我们也可以利用磁盘的性能来进行消耗。对象存储可以是java对象。避免了不必要的数据序列化和反序列化开销。
- 依赖关系:在RDD中我们会进行一系列的操作如map,filte,Join 等,但是不同的操作会使我们在操作中产生不同的依赖关系,主要分为两种 款依赖和窄依赖。
- 宽依赖:表现为一个父RDD的分区对应一个子分区 形成或者多个父RDD对应一个子RDD的分区,是一对一或者多对一的关系。
- 窄依赖:在这里就是一个父RDD对应多个子RDD 。
一般我们在操作中,map,filter,union 等操作属于窄依赖,而groupByKey,sortByKey等属于宽依赖,但是对于左外链接,又外链接,join链接等操作就需要分两种情况了。
- 假如我们在输入数据的时候,已经把数据进行了协同划分,比如我们在数据处理的时候进行的了根据键值分区,把属于多个父RDD的其中一个区的key落在了子RDD的一个分区里面,不产生在父RDD的一个分区落在子RDD的两个分区的情况。
- 对输入我们进行非协同划分,这种就属于宽依赖。
- 这两种区别 : 正如我们上面所说Spark 有高效的容错性,正式由于这种依赖关系所形成的,通过血缘图我们可以获取足够的信息来重新进行计算和恢复丢失数据分区的数据,提高性能。对于性能而言,窄依赖的失败恢复比较高效,因为他只需要根据自己的父节点进行数据分区恢复即可,但是宽依赖就不行了,需要重新计算过程设计到的父RDD分区,性能损耗大。但是Spark还提供了数据检查节点和记录日志,用于持久化数据RDD,减少追寻数据到最开始的RDD中。
- 阶段进行划分
- Spark在运行过程中,是分析各个阶段的RDD形成DAG操作,在通过分析各个RDD之间的依赖关系来决定如何划分阶段。具体的划分方法是:在DAG之间进行反向解析,从输出数据到数据库或者形成集合那个位置开始向上解析,遇到宽依赖就断开,聚到窄依赖就把当前的RDD加入到当前的阶段中。将窄依赖尽量划分到同一阶段中,可以实现流水线的操作。
- 总结上面的操作流程就是 :
- 创建RDD对象
- SparkContext负责构建RDD之间的依赖关系也就是血缘图。构建DAG
-DAGScheduler负责将DAG分解成多个阶段。每个阶段都包含多个任务,每个任务都会被调度器发送给工作节点上的Executor执行
![执行过程](https://i-blog.csdnimg.cn/blog_migrate/b9eb104808512b7f2715dfee2f1bd93d.webp?x-image-process=image/format,png)