RDD实现详解

来源:互联网 发布:蛋壳公寓靠谱吗 知乎 编辑:程序博客网 时间:2024/05/17 22:47
RDDSpark最基本也是最根本的数据抽象, 它具备像MapReduce等数据流模型的容错性, 并且允许开发人员在大型集群上执行
基于内存的计算。 现有的数据流系统对两种应用的处理并不高效: 一是迭代式算法, 这在图应用和机器学习领域很常见; 二是
交互式数据挖掘工具。 这两种情况下, 将数据保存在内存中能够极大地提高性能。 为了有效地实现容错,
RDD提供了一种高度
受限的共享内存, 即
RDD是只读的, 并且只能通过其他RDD上的批量操作来创建。 尽管如此,RDD仍然足以表示很多类型的计
算, 包括
MapReduce和专用的迭代编程模型( 如Pregel) 等。Spark实现的RDD在迭代计算方面比Hadoop20多倍, 同时还可以

5~7秒内交互式地查询1TB数据集。

概述
Spark的目标是为基于工作集的应用( 即多个并行操作重用中间结果的应用) 提供抽象, 同时保持MapReduce及其相关模型的优
势特性, 即自动容错、 位置感知性调度和可伸缩性。
RDD比数据流模型更易于编程, 同时基于工作集的计算也具有良好的描述
能力。
在这些特性中, 最难实现的是容错性。 一般来说, 分布式数据集的容错性有两种方式: 数据检查点和记录数据的更新。 我们面
向的是大规模数据分析, 数据检查点操作成本很高: 需要通过数据中心的网络连接在机器之间复制庞大的数据集, 而网络带宽
往往比内存带宽低得多, 同时还需要消耗更多的存储资源( 在内存中复制数据可以减少需要缓存的数据量, 而存储到磁盘则会
降低应用程序速度) 。 所以, 我们选择记录更新的方式。 但是, 如果更新太多, 记录更新成本也不低。 因此,
RDD只支持粗粒
度转换, 即在大量记录上执行的单个操作。 将创建
RDD的一系列转换记录下来( 即Lineage) , 以便恢复丢失的分区。
虽然只支持粗粒度转换限制了编程模型, 但
RDD仍然可以很好地适用于很多应用, 特别是支持数据并行的批量分析应用, 包括
数据挖掘、 机器学习、 图算法等, 因为这些程序通常都会在很多记录上执行相同的操作。
RDD不太适合那些异步更新共享状态
的应用, 例如并行
Web网络爬虫。 因此,Spark的目标是为大多数分析型应用提供有效的编程模型, 而其他类型的应用则交给
专门的系统。

什么是RDD
什么是RDDRDD是只读的、 分区记录的集合。RDD只能基于在稳定物理存储中的数据集和其他已有的RDD上执行确定性操作
来创建。 这些确定性操作称为转换, 如
mapfiltergroupByjoinRDD不需要物化。RDD含有如何从其他RDD衍生( 即计算)
出本
RDD的相关信息( 即Lineage) , 因此在RDD部分分区数据丢失的时候可以从物理存储的数据计算出相应的RDD分区。
RDD支持基于工作集的应用, 同时具有数据流模型的特点: 自动容错、 位置感知性调度和可伸缩性。RDD允许用户在执行多个
查询时显式地将工作集缓存在内存中, 后续的查询能够重用工作集, 这极大地提升了查询速度。
每个
RDD5个主要的属性:
1) 一组分片(Partition) , 即数据集的基本组成单位。 对于RDD来说, 每个分片都会被一个计算任务处理, 并决定并行计算的
粒度。 用户可以在创建
RDD时指定RDD的分片个数, 如果没有指定, 那么就会采用默认值。 默认值就是程序所分配到的CPU
Core
的数目。 图3-1描述了分区存储的计算模型, 每个分配的存储是由BlockManager实现的。 每个分区都会被逻辑映射成
BlockManager的一个Block, 而这个Block会被一个Task负责计算。
2) 一个计算每个分区的函数。SparkRDD的计算是以分片为单位的, 每个RDD都会实现compute函数以达到这个目的。
compute函数会对迭代器进行复合, 不需要保存每次计算的结果。 详情请参阅3.4.5节。
3RDD之间的依赖关系。RDD的每次转换都会生成一个新的RDD, 所以RDD之间就会形成类似于流水线一样的前后依赖关
系。 在部分分区数据丢失时,
Spark可以通过这个依赖关系重新计算丢失的分区数据, 而不是对RDD的所有分区进行重新计
算。
4) 一个Partitioner, 即RDD的分片函数。 当前Spark中实现了两种类型的分片函数, 一个是基于哈希的HashPartitioner, 另外一个
是基于范围的
RangePartitioner。 只有对于key-valueRDD, 才会有Partitioner, 非key-valueRDDParititioner的值是None
Partitioner函数不但决定了RDD本身的分片数量, 也决定了parent RDD Shuffle输出时的分片数量。
5) 一个列表, 存储存取每个Partition的优先位置(preferred location) 。 对于一个HDFS文件来说, 这个列表保存的就是每个
Partition所在的块的位置。 按照移动数据不如移动计算的理念,Spark在进行任务调度的时候, 会尽可能地将计算任务分配到
其所要处理数据块的存储位置。


RDD的创建
可以通过两种方式创建RDD
1) 由一个已经存在的Scala集合创建。
2) 由外部存储系统的数据集创建, 包括本地的文件系统, 还有所有Hadoop支持的数据集, 比如HDFSCassandraHBase
Amazon S3等。
RDD创建后, 就可以在RDD上进行数据处理。RDD支持两种操作: 转换(trans-formation) , 即从现有的数据集创建一个新的数
据集; 动作(
action) , 即在数据集上进行计算后, 返回一个值给Driver程序。 例如,map就是一种转换, 它将数据集每一个元
素都传递给函数, 并返回一个新的分布式数据集表示结果。 另一方面,
reduce是一种动作, 通过一些函数将所有元素叠加起
来, 并将最终结果返回给
Driver( 还有一个并行的reduceByKey, 能返回一个分布式数据集) 。
3-2描述了从外部数据源创建RDD, 经过多次转换, 通过一个动作操作将结果写回外部存储系统的逻辑运行图。 整个过程的
计算都是在
Worker中的Executor中运行。
RDD的转换
RDD中的所有转换都是惰性的, 也就是说, 它们并不会直接计算结果。 相反的, 它们只是记住这些应用到基础数据集( 例如一个文件) 上的
转换动作。 只有当发生一个要求返回结果给
Driver的动作时, 这些转换才会真正运行。 这个设计让Spark更加有效率地运行。 例如我们可以实
现: 通过
map创建的一个新数据集, 并在reduce中使用, 最终只返回reduce的结果给Driver, 而不是整个大的新数据集。 图3-3描述了RDD在进行
groupByRey时的内部RDD转换的实现逻辑图。 

groupByKey的操作中, 会在MapPartitionsRDD做一次Shuffle, 图3-3中设置的分片数量是3, 因此ShuffledRDD会有3个分片,ShuffledRDD实际上
仅仅是从上游的任务中读取
Shuffle的结果, 因此图的箭头是指向上游的MapPartitionsRDD的。 关于Shuffle的实现实际上要比图中展示得复杂得
多, 具体的实现细节可以参阅第
7章。
reduceByKeygroupByKey的实现差不多, 它在Shuffle完成之后, 需要做一次reduce


默认情况下, 每一个转换过的RDD都会在它执行一个动作时被重新计算。 不过也可以使用persist( 或者cache) 方法, 在内存中持久化一个
RDD。 在这种情况下, Spark将会在集群中保存相关元素, 下次查询这个RDD时能更快访问它。 也支持在磁盘上持久化数据集, 或在集群间复
制数据集, 这些选项将在下一节进行描述。

RDD支持的转换 


RDD的动作



1 0
原创粉丝点击