弹性分布式数据集RDD表示一个分区数据元素的集合,可以在其上进行并行操作。它是Spark的主要数据抽象概念。它是Spark库中定义的一个抽象类。
1.设计背景
许多迭代式算法(比如机器学习、图算法等)和交互式数据挖掘工具,共同之处是,不同计算阶段之间会重用中间结果。目前的MapReduce
框架都是把中间结果写入到HDFS
中,带来了大量的数据复制、磁盘IO和序列化开销。*RDD
就是为了满足这种需求而出现的,它提供了一个抽象的数据架构,我们不必担心底层数据的分布式特性,只需将具体的应用逻辑表达为一系列转换处理,不同RDD
之间的转换操作形成依赖关系,可以实现管道化*,避免中间数据存储。
Spark和MapReduce系统解决办法的思路相反,它设计了统一的编程抽象----弹性分布式数据集(RDD),这种全新的模型可以令用户直接控制数据的共享,使得用户可以指定数据存储到硬盘还是内存、控制数据的分区方法和数据集上进行的操作。RDD不仅增加了高效的数据共享原语,而且大大增加了其通用性。
2.RDD概念
Spark编程模型是弹性分布式数据集(Resilient Distributed Dataset,RDD
),它是MapReduce
模型的扩展和延伸,但它解决了MapReduce
的缺陷:在并行计算阶段高效地进行数据共享。
一个RDD
就是一个分布式对象集合,本质上是一个只读的分区记录集合,每个RDD
可分成多个分区,每个分区就是一个数据集片段,并且一个RDD
的不同分区可以被保存到集群中不同的节点上,从而可以在集群中的不同节点上进行并行计算。RDD
提供了一种高度受限的共享内存模型,即RDD
是只读的记录分区的集合,不能直接修改,只能基于稳定的物理存储中的数据集创建RDD
,或者通过在其他RDD
上执行确定的转换操作(如map
、join
和group by
)而创建得到新的RDD
。
RDD概念的来源论文:The original paper that gave birth to the concept of RDD is Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing by Matei Zaharia, et al.
2.1RDD运行过程
RDD
典型的执行过程如下:
- 1、
RDD
读入外部数据源进行创建,利于使用textFile
函数加载本地数据; - 2、
RDD
经过一系列的转换(Transformation
)操作,每一次都会产生不同的RDD
,供给狭义转换作使用,这里的Transformation
操作就是filter
函数; - 3、最后一个
RDD
经过“动作”(Action
)操作进行转换,并输出到外部数据源。
RDD的创建和转换方法都是惰性操作。当Spark应用调用操作方法或者保存RDD至存储系统的时候,RDD的转换计算才真正执行。惰性操作的好处:惰性操作使得Spark可以高效的执行RDD计算。直到Spark应用需要操作结果时才进行计算,Spark可以利用这一点优化RDD操作。这使得操作流水线化,而且还避免在网路间不必要的数据传输。
这一系列处理称为一个Lineage
(血缘关系),即DAG
拓扑排序的结果优点:
- 惰性调用、管道化、避免同步等待、不需要保存中间结果、每次操作变得简单
3.RDD特性
总体而言,Spark采用RDD
以后能够实现高效计算的原因主要在于:
- 1、高效的容错性。
- 传统方式:现在的分布式共享内存、键值存储、内存数据库等,为了实现容错,必须在集群节点之间进行数据复制或者记录日志,也就是在节点之间会发生大量的数据传输,这对于数据密集型应用而言会带来很大的开销。
-
RDD
方式:在RDD
的设计中,数据只读,不可修改,如果要修改数据,必须从父RDD
转换到子RDD
,由此在不同的RDD
之间建立了血缘关系。所以,RDD
是一种天生具有具有容错机制的特殊集合,不需要通过数据冗余的方式(比如检查点)实现容错,而只需要通过RDD
父子依赖(血缘)关系重算计算得到丢失的分区来实现容错,无须回滚整个系统,这样就避免了数据复制的高开销,而且重算过程可以在不同节点并行进行,实现了高效的容错。 - 此外:
RDD
提供的转换操作都是一些粗粒度的操作(比如map、filter和join),RDD依
赖关系只需要记录这种粗粒度的转换操作,而不需要记录具体的数据和各种细粒度操作的日志,这就大大降低了数据密集型应用中容错开销。
- 2、中间结果持久化到内存,数据在内存中的多个
RDD
操作之间进行传递,不需要“落地”到磁盘上,避免了不必要的读写磁盘开销。 - 3、存放的数据可以是Java对象,避免了不必要的对象序列化和反序列化。
4.RDD之间的依赖关系
RDD
是易转换、已操作的,这意味着用户可以从已有的RDD
转换出新的RDD
转换出新的RDD
。新、旧RDD
之间必定存在这某种联系,这种联系称为RDD
依赖关系。
- 窄依赖:
父RDD
的每个子分区最后被其子RDD
的一个分区所依赖,也就是说子RDD
的每个分区依赖于常数个父分区,子RDD
每个分区的生成与父RDD
的数据规模无关。 - 宽依赖:
父RDD
的每个分区被其子RDD
的多个分区所依赖,子RDD
每个分区的生成与父RDD
的数据规模相关。
5.Stage的划分
Spark通过分析各个RDD的依赖关系生成了DAG
,再通过分析各个RDD
中的分区之间的依赖关系来决定如何划分Stage
,具体划分方法是:
- 在
DAG
中进行反向解析,遇到宽依赖就断开 - 遇到窄依赖就把当前的
RDD
加入到Stage
中 - 将窄依赖尽量划分在同一个
Stage
中,可以实现流水线计算
5.1流水线操作实例
分区7通过map
操作生成的分区9,可以不用等待分区8到分区10这个map
操作的计算结束,而是继续进行union
操作,得到分区13,这样流水线执行大大提高了计算的效率。RDD被分成三个Stage
,在Stage2
中,从map
到union
都是窄依赖,这两步操作可以形成一个流水线操作。
6.RDD运行过程
通过上述对RDD
概念、依赖关系和Stage
划分的介绍,结合之前介绍的Spark运行基本流程,再总结一下RDD
在Spark架构中的运行过程:
- 1、创建RDD对象;
- 2、
SparkContext
负责计算RDD
之间的依赖关系,构建DAG
; - 3、
DAGScheduler
负责把DAG
图分解成多个Stage
,每个Stage
中包含了多个Task
,每个Task
会被TaskScheduler
分发给各个WorkerNode
上的Executor
去执行。
网友评论