Spark之RDD(弹性分布式数据集) 详解

1.RDD简述

RDD简单的解释:
RDD是将数据项拆分为多个分区的集合,存储在集群的工作节点上的内存和磁盘中,并执行正确的操作。
复杂的解释:
RDD是用于数据转换的接口,如map算子、filter算子;
RDD指向了存储在HDFS、Cassandra、 HBase等、 或缓存
(内存、内存+磁盘、仅磁盘等),或在故障或缓存收回时重新计算其他RDD分区中的数据

RDD(Resilient Distributed Dataset):弹性分布式数据集,Spark计算的基石,为用户屏蔽了底层对数据的复杂抽象和处理,为用户提供了一组方便的数据转换与求值方法。

Spark 的核心是建立在统一的抽象 RDD 之上,基于 RDD 的转换和行动操作使得 Spark 的各个组件可以无缝进行集成,从而在同一个应用程序中完成大数据计算任务。

RDD 提供了一个抽象的数据架构,从而让开发者不必担心底层数据的分布式特性,只需将具体的应用逻辑表达为一系列转换处理,不同 RDD 之间的转换操作形成依赖关系,可以实现管道化,从而避免了中间结果的存储,大大降低了数据复制、磁盘 IO 和序列化开销。

2.RDD特点

  1. 分布式( Distributed)
    数据的计算并非只局限于单个节点,而是多个节点之间协同计算得到
  2. 分区
    RDD是分区的,RDD里面的具体数据是分布在多台机器上的Executor里面的。堆内内存和堆外内存+磁盘。
  3. 不可变
    RDD是不可变的,如果需要在一个RDD上进行转换操作,则会生成一个新的RDD
  4. 数据集
    RDD是只读的、分区记录的集合,每个分区分布在集群的不同节点上;
    RDD并不存储真正的数据,只是对数据和操作的描述
  5. 弹性(Resilient)
    自动进行存储方式的切换 RDD优先存放在内存中,当内存不足,Spark自动将RDD写入磁盘
  6. 容错性
    根据数据血统,可以自动从节点失败中恢复分区;
    基于Linage的高效容错机制,在任何时候都能进行重算,根据数据血统,可以自动从节点失败中恢复分区,各个分片之间的数据互不影响
  7. 计算
    计算是分层的,有应用->Job->Stage->TaskSet->Task,每一层都有对应的计算的保障与重复机制,保障计算不会由于一些突发因素而终止。
  8. 分片
    可以根据业务需求或者一些算子来重新调整RDD中的数据分布。

3.RDD运行原理

一个 RDD 就是一个分布式对象集合,提供了一种高度受限的共享内存模型,其本质上是一个只读的分区记录集合,不能直接修改。
每个 RDD 可以分成多个分区,每个分区就是一个数据集片段,并且一个 RDD 的不同分区可以保存到集群中不同的节点上,从而可以在集群中的不同节点上进行并行计算。

RDD 提供了一组丰富的操作以支持常见的数据运算,分为“行动”(Action)和“转换”(Transformation)两种类型,前者用于执行计算并指定输出的形式,后者指定 RDD 之间的相互依赖关系。

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

RDD 的执行过程:

  1. 读入外部的数据源(或者内存中的集合)进行 RDD 创建;
  2. RDD 经过一系列的 “转换” 操作,每一次都会产生不同的 RDD,供给下一个转换使用;
  3. 最后一个 RDD 经过 “行动” 操作进行处理,并输出指定的数据类型和值。

总结:
RDD 采用了惰性调用,即在 RDD 的执行过程中,所有的转换操作都不会执行真正的操作,只会记录依赖关系,而只有遇到了行动操作,才会触发真正的计算,并根据之前的依赖关系得到最终的结果。
如下图所示来举例说明RDD的执行过程:开始从输入中创建了两个 RDD,分别是 A 和 C,然后经过一系列的转换操作,最终生成了一个 F,这也是一个 RDD。

注意,这些转换操作的执行过程中并没有执行真正的计算,基于创建的过程也没有执行真正的计算,而只是记录的数据流向轨迹。
当 F 执行了行为操作并生成输出数据时,Spark 才会根据 RDD 的依赖关系生成有向无环图(DAG),并从起点开始执行真正的计算。
正是 RDD 的这种惰性调用机制,使得转换操作得到的中间结果不需要保存,而是直接管道式的流入到下一个操作进行处理。
Spark之RDD(弹性分布式数据集) 详解_第1张图片

4.RDD五大特性

  1. RDD由很多partition构成,在spark中,计算时,有多少partition 就对应有多少个task来执行
  2. 算子作用在partition上
  3. RDD之间存在一系列依赖关系
  4. 分区器决定数据(key-value)分配至哪个分区,如果RDD里面存的数据是key-value形式,则可以传递一个自定义的Partitioner进行重新分区,比如可以按key的hash值分区
  5. partiton提供最佳的计算位置,优先位置列表,将计算任务分派到其所在处理数据块的存储位置(移动数据不如移动计算)

5.RDD之间的依赖关系(宽依赖与窄依赖)

RDD 中的不同的操作会使得不同 RDD 中的分区会产生不同的依赖关系,主要分为窄依赖(Narrow Dependency)与宽依赖(Wide Dependency)

其中,窄依赖表示的是父 RDD 和子 RDD 之间的一对一关系或者多对一关系,主要包括的操作有 map、filter、union 等;

宽依赖则表示父 RDD 与子 RDD 之间的一对多关系,即一个父 RDD 转换成多个子 RDD,主要包括的操作有 groupByKey、sortByKey 等。

Spark之RDD(弹性分布式数据集) 详解_第2张图片
对于窄依赖的 RDD,可以以流水线的方式计算所有父分区,不会造成网络之间的数据混合。
对于宽依赖的 RDD,则通常伴随着 Shuffle 操作,即首先需要计算好所有父分区数据,然后在节点之间进行 Shuffle。
因此,在进行数据恢复时,窄依赖只需要根据父 RDD 分区重新计算丢失的分区即可,而且可以并行地在不同节点进行重新计算。
而对于宽依赖而言,单个节点失效通常意味着重新计算过程会涉及多个父 RDD 分区,开销较大。
此外,Spark 还提供了数据检查点和记录日志,用于持久化中间 RDD,从而使得在进行失败恢复时不需要追溯到最开始的阶段。在进行故障恢复时,Spark 会对数据检查点开销和重新计算 RDD 分区的开销进行比较,从而自动选择最优的恢复策略。

6.划分阶段

Spark 通过分析各个 RDD 的依赖关系生成了 DAG ,再通过分析各个 RDD 中的分区之间的依赖关系来决定如何划分阶段;
具体划分方法是:在 DAG 中进行反向解析,遇到宽依赖就断开,遇到窄依赖就把当前的 RDD 加入到当前的阶段中;将窄依赖尽量划分在同一个阶段中,可以实现流水线计算。

例如在下图中,首先根据数据的读取、转化和行为等操作生成 DAG。然后在执行行为操作时,反向解析 DAG,由于从 A 到 B 的转换和从 B、F 到 G 的转换都属于宽依赖,则需要从在宽依赖处进行断开,从而划分为三个阶段。

把一个 DAG 图划分成多个 “阶段” 以后,每个阶段都代表了一组关联的、相互之间没有 Shuffle 依赖关系的任务组成的任务集合。每个任务集合会被提交给任务调度器(TaskScheduler)进行处理,由任务调度器将任务分发给 Executor 运行。
Spark之RDD(弹性分布式数据集) 详解_第3张图片

7.RDD运行过程

RDD 在 Spark 架构中的运行过程(如下图所示):

  1. 创建 RDD 对象;
  2. SparkContext 负责计算 RDD 之间的依赖关系,构建 DAG;
  3. DAGScheduler 负责把 DAG 图反向解析成多个阶段,每个阶段中包含多个任务,每个任务会被任务调度器分发给工作节点上的 Executor 上执行。
    Spark之RDD(弹性分布式数据集) 详解_第4张图片

8.RDD的创建

1.使用集合创建RDD

sc.parallelize(seq,n):把seq这个集合并行化分片到节点,n可以指定几个分区
sc.makeRDD(seq):把seq这个集合并行化分片到节点,它的实现就是parallelize
sc.makeRDD(seq[(T,seq)]):这种方式可以指定RDD的存放位置

注意:

  • Spark默认会根据集群的情况来设置分区的数量,也可以通过parallelize()第二参数来指定

  • Spark会为每一个分区运行一个任务进行处理

2. 通过加载文件产生RDD

文件中的一行文本作为RDD的一个元素
val distFile=sc.textFile(“file:///home/hadoop/data/a.txt”)

注意:

  • 加载“file://……”文件时,以local运行仅需一份本地文件,以Spark集群方式运行,应保证每个节点均有该文件的本地副本

  • 读取的文件支持目录、压缩文件以及通配符
    比如:

sc.textFile("/my/file")
sc.textFile("/my/file/*.txt")
sc.textFile("/my/file/*.gz")
  • Spark默认访问HDFS
  • Spark默认为HDFS文件的每一个数据块创建一个分区,也可以通过textFile()第二个参数指定,但只能比数据块数量多

3. 创建PairRDD的方法

SparkContext.wholeTextFiles():
可以针对一个目录中的大量小文件返回作为PairRDD
普通RDD:org.apache.spark.rdd.RDD[data_type]
PairRDD:org.apache.spark.rdd.RDD[(key_type,value_type)]

Spark 为包含键值对类型的 RDD 提供了一些专有的操作,比如:reduceByKey()、groupByKey()……
也可以通过键值对集合创建PairRDD:sc.parallelize(List((1,2),(1,3)))

4,其他创建RDD的方法

SparkContext.sequenceFile[K,V]()

Hadoop SequenceFile的读写支持

SparkContext.hadoopRDD()、newAPIHadoopRDD()

从Hadoop接口API创建

SparkContext.objectFile()

RDD.saveAsObjectFile()的逆操作

9.RDD的操作

RDD中操作分为两大类型:转换(transformation)和行动(action)
转换:
通过操作将RDD转换成另外一个RDD;
对于转换操作,RDD的所有转换都不会直接计算结果,仅记录作用于RDD上的操作,当遇到动作算子(Action)时才会进行真正计算

行动:
将一个RDD进行求值或者输出;
本质上动作算子通过SparkContext执行提交作业操作,触发RDD DAG(有向无环图)的执行,所有的动作算子都是急迫型(non-lazy),RDD遇到Action就会立即计算

总结:
所有这些操作主要针对两种类型的RDD:数值RDD和键值对RDD
RDD的所有转换操作都是懒执行的,只有当行动操作出现的时候spark才会真的去运行

RDD的基本讲解就到这里,后面小编会给大家着重说下转换算子和行动算子,记得关注小编!!

你可能感兴趣的:(spark,spark)