大数据(十三)--初识SparkCore之RDD(弹性分布式数据集)&Operation(算子)

  在开始学习Spark工作原理之前, 先来介绍一下Spark中两个最为重要的概念-- 弹性分布式数据集(Resilient Distributed Datasets, RDD) 和算子(Operation).

RDD背景

  Spark的核心是建立在RDD之上, 使Spark中的各个组件可以无缝进行集成, 从而在一个应用程序中完成大数据计算. 这也是为什么说在SparkCore中一切得计算都是基于RDD来完成的. RDD的设计理念源自AMP实验室发表的论文–Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing.

  MapReduce计算框架在实际应用中, 许多迭代式算法和交互式数据挖掘过程中的计算结果会写到磁盘, 然后再重复使用, 这就带来了大量的磁盘IO和序列化开销. 为解决中间过程数据落地花费大量时间的需求, 出现了一种抽象的数据结构, 让我们不必再考虑数据的分布式特性, 只需保存具体的逻辑转换表达式即可, 这种数据结构就是RDD.

  RDD之间的转换操作使父子RDD之间具有依赖关系, 满足条件的RDD之间形成管道(Pipeline), 从而避免中间结果落地, 极大的降低了磁盘IO和序列化消耗的时间.

RDD介绍

  RDD(弹性分布式数据集), 虽然叫做数据集, 但RDD并不像集合一样存储真实的数据, 而是存储这些数据转换的逻辑, 可以将RDD理解为一个大的数据集合以分布式的形式保存在集群服务器的内存中. 每个RDD可以分成多个分区, 每个分区就是一个数据集片段, 并且一个RDD的不同分区可以被保存到集群中不同的节点上(但是同一个分区不能被拆分保存), 从而可以在集群中的不同节点上进行并行计算.

  RDD提供了一种高度受限的共享内存模型, 即RDD是只读的记录分区的集合, 不能直接修改, 只能基于稳定的物理存储中的数据集来创建RDD, 或者通过在其他RDD上执行转换操作(如map、join和groupBy) 创建得到新的RDD.

Operation介绍

  算子(Operation)是Spark中定义的函数, 用于对RDD中的数据结构进行操作和转换等. Spark中的算子可以分为4类:

  1. 创建类(creation)算子, 用于将内存中的集合或外部文件创建为RDD.
  2. 转换(transformation)算子, 用于将一种格式的RDD转换为其他自定义格式.
  3. 缓存(cache)算子, 用于将RDD缓存在内存(memory)或磁盘(disk)中, 一般后续计算会用到重复数据时才会使用.
  4. 行动(action)算子, 用于触发执行Spark作业, 并将计算结果保存为集合, 标量或保存到外部文件, 数据库中.

  典型的RDD执行过程如下:
在这里插入图片描述

  1. 读入外部数据源(或者内存中的集合) ,然后Create RDD;
  2. RDD经过一系列Transformation, 每一次都会产生不同的RDD, 供给下一个Transformation 使用;
  3. 最后一个RDD经Action进行处理, 得到最后想要的值, 并进行后续输出操作.

  需注意: RDD采用惰性调用, 即在RDD的执行过程中, 如图所示, 真正的计算发生在RDD的Action操作, 对于Action之前的所有Transformation操作, Spark只是记录下Transformation操作应用的一些基础数据集以及RDD生成的轨迹, 即相互之间的依赖关系, 而不会触发真正的计算.

  RDD提供的转换接口都非常简单, 都是类似map, filter, groupBy, join等粗粒度的数据转换操作, 而不是针对某个数据项的细粒度修改. 因此, RDD比较适合对于数据集中元素执行相同操作的批处理式应用, 而不适合用于需要异步/细粒度状态的应用, 比如Web应用系统, 增量式的网页爬虫等.

  转换和行动两种类型的算子, 前者指定RDD之间的相互依赖关系, 后者用于执行计算并指定输出的形式. 两类操作的主要区别是, 转换操作接受RDD并返回RDD, 而行动操作(如count、collect等) 接受RDD但是返回非RDD(即输出一个值或结果).

RDD五大特性

  1. RDD是由一系列的Partition(分区)组成;
  2. 一个函数作用在每一个分区上;
  3. RDD之间存在依赖关系;
  4. [可选项]分区器作用在KV格式的RDD上;
  5. [可选项]RDD会提供最佳计算位置.

  接下来, 结合Spark实现的WC案例, 来理解这五个特性以及其他注意点(图中绿色为block块, 蓝色为Partition分区):
在这里插入图片描述

  • HDFS存储文件是以block块的形式, Spark应用在读取HDFS上的数据后, 会将同一个block块中的数据转换逻辑保存在同一个Partition中, 一个文件对应的所有Partition构成一个RDD. 即一个RDD中的Partition个数等于这个文件存储在HDFS中的block个数. 但有一个例外, 如果一个block块的最后存储了某个数据的大部分字节后达到block规定的大小, 仅有少量字节存储在另外一个block块中, 这时这多余的小部分数据会放在与大部分数据相同的Partition中, 即Partition数小于block块数.
  • Spark中没有读文件的方法, 但Spark依然能够读取文件内容依赖的是MapReduce中读文件的方法. MR读文件前, 会先将文件划分为一个个的split(切片), 一个split的大小 = 一个block的大小; 但这个文件的split个数 ≈ 存储这个文件的block个数(同上一个例外情况); 一个RDD中Partition的个数 = 这个文件切分的split个数.
  • 每一个函数作用在每一个分区上, 即每个函数会在每一个Partition中各执行一次.
  • RDD之间存在依赖关系, 通过一个算子关联的两个RDD称为父子RDD, 父子RDD之间存在宽窄依赖(后续讲解), 子RDD知道它的父RDD是谁, 但父RDD不知道它的子RDD有谁. 这种依赖关系的优势在于当数据因某种情形丢失时, 可以通过算子和父RDD重写计算出子RDD, 从而提高了计算的容错性. (RDD的依赖关系也被称为RDD的血统–Lineage)
  • KV格式的RDD指RDD中的数据是二元组类型, 对于这类RDD可以使用分区器按照Key或者Value进行分组, 进而完成聚合计算. 在WC中, pairRDD和restRDD均为KV格式的RDD. 分区器用于决定数据被放到哪一个reduce task中处理.
  • 每一个算子作用在每一个Partition上, Partition会分布式的存储在集群各个节点的内存中, 对一个Partition的连续处理可以看作是一个task任务, 每一个task计算任务都在数据所在节点上执行, 从而实现数据本地化, 减少网络IO. 简单来说, RDD会提供一个方法接口, 调用这个接口就能直接拿到这个RDD所有Partition的位置, 拿到位置之后就可以分发task了. 至于这个接口是什么不需要我们关心, Spark应用在执行时会自动寻找.

转载于:https://www.cnblogs.com/skisqibao/p/10278971.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值