(三)Spark core之RDD

9 篇文章 0 订阅

1.什么是RDD?

RDD(Resilient Distributed Datasets,弹性分布式数据集),是Spark最为核心的概念,RDD是一个只读的有属性的数据集。属性用来描述当前数据集的状态,数据集是由数据的分区(partition)组成,并(由block)映射成真实数据。RDD属性包括名称、分区类型、父RDD指针、数据本地化、数据依赖关系等。RDD是理解Apache Spark 工作原理的最佳入口之一。它代表一个不可变、可分区、里面的元素可并行计算的集合。RDD具有数据流模型的特点:自动容错、位置感知性调度和可伸缩性。RDD允许用户在执行多个查询时显式地将工作集缓存在内存中,后续的查询能够重用工作集,这极大地提升了查询速度。

2.RDD的属性

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

(2)sparkcontext  SparkContext为Spark job的入口,由Spark driver创建在client端,包括集群连接,RddID,创建抽样,累加器,广播变量等信息。

(3)迭代器(iterator),用来查找当前RDD Partition与父RDD中Partition的血缘关系。并通过StorageLevel确定迭代位置,直到确定真实数据的位置。迭代方式分为checkpoint迭代和RDD迭代, 如果StorageLevel为NONE则执行computeOrReadCheckpoint计算并获取数据,此方法也是一个迭代器,迭代checkpoint数据存放位置,迭代出口为找到真实数据或内存。如果Storagelevel不为空,根据存储级别进入RDD迭代器,继续迭代父RDD的结构,迭代出口为真实数据或内存。迭代器内部有数据本地化判断,先从本地获取数据,如果没有则远程查找。

(4)RDD之间的依赖关系。RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。这就是spark的容错性的体现。spark基于Lineage的高效容错(第n个节点出错,会从第n-1个节点恢复,血统容错);

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

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

图解RDD

RDD是怎样形成的?
有需要在Spark内计算的数据即形成RDD,所以开始输入到Spark的数据和经过Spark算子(下文介绍算子)计算过的数据都会形成RDD,包括即将输出的数据也会生成RDD后统一输出的。如图:

关于RDD的形成, 主要是通过连接物理存储介质输入的数据集和在已有RDD基础上进行相关计算操作衍生的。也可以理解为是通过读取文件、通过并行化的方式创建RDD以及通过其他的RDD转换的方式生成

简单解释下这几行代码:
第一行,从HDFS上读取in.txt文件,创建了第一个RDD
第二行,按空格分词,扁平化处理,生成第二个RDD,每个词计数为1,生成了第三个RDD。这里可能有人会问,为什么生成了两个RDD呢,因为此行代码RDD经过了两次算子转换(transformation)操作。关于算子这里不多详述,请关注下期文章。
第三行,按每个词分组,累加求和,生成第四个RDD
第四行,将Wordcount统计结果输出到HDFS
整个产生过程如下图所示:

通过上文的例子可以了解到,一个作业从开始到结束的计算过程中产生了多个RDD,RDD之间是彼此相互依赖的,我们把这种父子依赖的关系,称之为“血统”。如果父RDD的每个分区最多只能被子RDD的一个分区使用,我们称之为(narrow dependency)窄依赖;若一个父RDD的每个分区可以被子RDD的多个分区使用,我们称之为(wide dependency)宽依赖。简单来讲窄依赖就是父子RDD分区间”一对一“的关系,宽依赖就是”一对多“关系,具体理解可参考下图:

那么为什么Spark要将依赖分成这两种呢,下面我们就了解下原因:
首先,从计算过程来看,窄依赖是数据以管道方式经一系列计算操作可以运行在了一个集群节点上,如(map、filter等),宽依赖则可能需要将数据通过跨节点传递后运行(如groupByKey),有点类似于MR的shuffle过程。
其次,从失败恢复来看,窄依赖的失败恢复起来更高效,因为它只需找到父RDD的一个对应分区即可,而且可以在不同节点上并行计算做恢复;宽依赖则牵涉到父RDD的多个分区,恢复起来相对复杂些。
综上, 这里引入了一个新的概念Stage。Stage可以简单理解为是由一组RDD组成的可进行优化的执行计划。如果RDD的衍生关系都是窄依赖,则可放在同一个Stage中运行,若RDD的依赖关系为宽依赖,则要划分到不同的Stage。这样Spark在执行作业时,会按照Stage的划分, 生成一个完整的最优的执行计划。下面引用一张比较流行的图片辅助大家理解Stage,如图RDD¬-A到RDD-B和RDD-F到RDD-G均属于宽依赖,所以与前面的父RDD划分到了不同的Stage中。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值