RDD---弹性分布式数据集

RDD的设计与运行原理

Spark的核心是建立在统一的抽象RDD上的,使得Spark的各个组件可以无缝地进行集成,在同一个应用程序中完成大数据计算任务。

一、RDD设计背景

在实际应用中,存在许多迭代式算法(如机器学习、图算法)和交互式数据挖掘工具,这些应用场景的共同之处是,不同计算阶段之间会重用中间结果,即一个阶段的输出结果会作为下一个阶段的输入。但是,目前的MapReduce框架都是把中间结果写入到HDFS上,带来了大量的数据复制、磁盘IO和序列化开销。虽然类似Pregel等图计算框架也是将结果保存到内存当中,但是这些框架只能支持一些特定的计算模式,并没有提供一种通用的数据抽象。RDD就是为了满足这种需求出现的,它提供了一个抽象的数据架构,我们不必担心底层数据的分布式特性,只需将具体的应用逻辑表达为一系列转换处理,不同的RDD之间的转换操作形成依赖关系,可以实现管道化,从而避免了中间结果的存储,大大降低了数据复制、磁盘IO和序列化开销。

二、RDD概念

1.什么是RDD?

RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变可分区、里面的元素可并行计算的集合。它是一种有容错机制的特殊数据集合,可以分布在集群的结点上,以函数式操作集合的方式进行各种并行操作。

通俗点来讲,可以将 RDD 理解为一个分布式对象集合,本质上是一个只读的分区记录集合。每个 RDD 可以分成多个分区,每个分区就是一个数据集片段。一个 RDD 的不同分区可以保存到集群中的不同结点上,从而可以在集群中的不同结点上进行并行计算。

RDD 的操作是惰性的,当 RDD 执行转化操作的时候,实际计算并没有被执行,只有当 RDD 执行行动操作时才会促发计算任务提交,从而执行相应的计算操作。

2.RDD的属性

(1)一组分片(Partition),即数据集的基本组成单位。对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建RDD时指定RDD的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的CPU Core的数目。

(2)一个计算每个分区的函数。Spark中RDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合,不需要保存每次计算的结果。

(3)RDD之间的依赖关系。RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。

(4)一个Partitioner,即RDD的分片函数。当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于于key-value的RDD,才会有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。

(5)一个列表,存储存取每个Partition的优先位置(preferred location)。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。

三、RDD特点

1.可分区

RDD逻辑上是分区的,每个分区的数据是抽象存在的,计算的时候会通过一个compute函数得到每个分区的数据。

2.不可变

如下图所示,RDD是只读的,要想改变RDD中的数据,只能在现有的RDD基础上生成新的RDD。

(由一个RDD转换到另一个RDD可以通过丰富的操作算子实现)
RDD的操作算子包括两类,一类叫做transformations(转换算子),它是用来将RDD进行转化,构建RDD的血缘关系;另一类叫做actions(行动算子),它是用来触发RDD的计算,得到RDD的相关计算结果或者将RDD保存的文件系统中。

3.依赖关系

RDD通过操作算子进行转换,转换得到的新RDD包含了从其他RDD衍生所必需的信息,RDD之间维护着这种血缘关系,也称之为依赖关系。
依赖有两种:宽依赖窄依赖
如下图所示,依赖包括两种,一种是窄依赖,RDDs之间分区是一一对应的,另一种是宽依赖,下游RDD的每个分区与上游RDD(也称之为父RDD)的每个分区都有关,是多对多的关系。

4.缓存(cache)

如果在应用程序中多次使用同一个RDD,可以将该RDD缓存起来,该RDD只有在第一次计算的时候会根据血缘关系得到分区的数据,在后续其他地方用到该RDD的时候,会直接从缓存处取而不用再根据血缘关系计算,这样就加速后期的重用。

5.检测点(CheckPoint)

虽然RDD的血缘关系天然地可以实现容错,当RDD的某个分区数据失败或丢失,可以通过血缘关系重建。但是对于长时间迭代型应用来说,随着迭代的进行,RDDs之间的血缘关系会越来越长,一旦在后续迭代过程中出错,则需要通过非常长的血缘关系去重建,势必影响性能。为此,RDD支持checkpoint将数据保存到持久化的存储中,这样就可以切断之前的血缘关系,因为checkpoint后的RDD不需要知道它的父RDDs了,它可以直接从checkpoint处拿到数据。

四、RDD的创建

1.通过并行化的方式创建RDD

2.读取文件生成RDD

3.通过其他RDD转换

通过RDD执行转换算子,转换生成新的RDD。

五、RDD运行过程


(1)创建RDD对象。
(2)SparkContext 负责计算 RDD 之间的依赖关系,构建 DAG。
(3)AGSchedule 负责把 DAG 图反向解析成多个阶段,每个阶段中包含多个任务,每个任务会被任务调度器分发给工作节点上的 Executor 上执行。

发布了13 篇原创文章 · 获赞 14 · 访问量 649

猜你喜欢

转载自blog.csdn.net/LCY_1013/article/details/105185377