RDD简单的解释:
RDD是将数据项拆分为多个分区的集合,存储在集群的工作节点上的内存和磁盘中,并执行正确的操作。
复杂的解释:
RDD是用于数据转换的接口,如map算子、filter算子;
RDD指向了存储在HDFS、Cassandra、 HBase等、 或缓存
(内存、内存+磁盘、仅磁盘等),或在故障或缓存收回时重新计算其他RDD分区中的数据
RDD(Resilient Distributed Dataset):弹性分布式数据集,Spark计算的基石,为用户屏蔽了底层对数据的复杂抽象和处理,为用户提供了一组方便的数据转换与求值方法。
Spark 的核心是建立在统一的抽象 RDD 之上,基于 RDD 的转换和行动操作使得 Spark 的各个组件可以无缝进行集成,从而在同一个应用程序中完成大数据计算任务。
RDD 提供了一个抽象的数据架构,从而让开发者不必担心底层数据的分布式特性,只需将具体的应用逻辑表达为一系列转换处理,不同 RDD 之间的转换操作形成依赖关系,可以实现管道化,从而避免了中间结果的存储,大大降低了数据复制、磁盘 IO 和序列化开销。
一个 RDD 就是一个分布式对象集合,提供了一种高度受限的共享内存模型,其本质上是一个只读的分区记录集合,不能直接修改。
每个 RDD 可以分成多个分区,每个分区就是一个数据集片段,并且一个 RDD 的不同分区可以保存到集群中不同的节点上,从而可以在集群中的不同节点上进行并行计算。
RDD 提供了一组丰富的操作以支持常见的数据运算,分为“行动”(Action)和“转换”(Transformation)两种类型,前者用于执行计算并指定输出的形式,后者指定 RDD 之间的相互依赖关系。
RDD 提供的转换接口都非常简单,都是类似 map 、filter 、groupBy 、join 等粗粒度的数据转换操作,而不是针对某个数据项的细粒度修改。因此,RDD 比较适合对于数据集中元素执行相同操作的批处理式应用,而不适合用于需要异步、细粒度状态的应用,比如 Web 应用系统、增量式的网页爬虫等。
RDD 的执行过程:
总结:
RDD 采用了惰性调用,即在 RDD 的执行过程中,所有的转换操作都不会执行真正的操作,只会记录依赖关系,而只有遇到了行动操作,才会触发真正的计算,并根据之前的依赖关系得到最终的结果。
如下图所示来举例说明RDD的执行过程:开始从输入中创建了两个 RDD,分别是 A 和 C,然后经过一系列的转换操作,最终生成了一个 F,这也是一个 RDD。
注意,这些转换操作的执行过程中并没有执行真正的计算,基于创建的过程也没有执行真正的计算,而只是记录的数据流向轨迹。
当 F 执行了行为操作并生成输出数据时,Spark 才会根据 RDD 的依赖关系生成有向无环图(DAG),并从起点开始执行真正的计算。
正是 RDD 的这种惰性调用机制,使得转换操作得到的中间结果不需要保存,而是直接管道式的流入到下一个操作进行处理。
RDD 中的不同的操作会使得不同 RDD 中的分区会产生不同的依赖关系,主要分为窄依赖(Narrow Dependency)与宽依赖(Wide Dependency)。
其中,窄依赖表示的是父 RDD 和子 RDD 之间的一对一关系或者多对一关系,主要包括的操作有 map、filter、union 等;
而宽依赖则表示父 RDD 与子 RDD 之间的一对多关系,即一个父 RDD 转换成多个子 RDD,主要包括的操作有 groupByKey、sortByKey 等。
对于窄依赖的 RDD,可以以流水线的方式计算所有父分区,不会造成网络之间的数据混合。
对于宽依赖的 RDD,则通常伴随着 Shuffle 操作,即首先需要计算好所有父分区数据,然后在节点之间进行 Shuffle。
因此,在进行数据恢复时,窄依赖只需要根据父 RDD 分区重新计算丢失的分区即可,而且可以并行地在不同节点进行重新计算。
而对于宽依赖而言,单个节点失效通常意味着重新计算过程会涉及多个父 RDD 分区,开销较大。
此外,Spark 还提供了数据检查点和记录日志,用于持久化中间 RDD,从而使得在进行失败恢复时不需要追溯到最开始的阶段。在进行故障恢复时,Spark 会对数据检查点开销和重新计算 RDD 分区的开销进行比较,从而自动选择最优的恢复策略。
Spark 通过分析各个 RDD 的依赖关系生成了 DAG ,再通过分析各个 RDD 中的分区之间的依赖关系来决定如何划分阶段;
具体划分方法是:在 DAG 中进行反向解析,遇到宽依赖就断开,遇到窄依赖就把当前的 RDD 加入到当前的阶段中;将窄依赖尽量划分在同一个阶段中,可以实现流水线计算。
例如在下图中,首先根据数据的读取、转化和行为等操作生成 DAG。然后在执行行为操作时,反向解析 DAG,由于从 A 到 B 的转换和从 B、F 到 G 的转换都属于宽依赖,则需要从在宽依赖处进行断开,从而划分为三个阶段。
把一个 DAG 图划分成多个 “阶段” 以后,每个阶段都代表了一组关联的、相互之间没有 Shuffle 依赖关系的任务组成的任务集合。每个任务集合会被提交给任务调度器(TaskScheduler)进行处理,由任务调度器将任务分发给 Executor 运行。
RDD 在 Spark 架构中的运行过程(如下图所示):
sc.parallelize(seq,n):把seq这个集合并行化分片到节点,n可以指定几个分区
sc.makeRDD(seq):把seq这个集合并行化分片到节点,它的实现就是parallelize
sc.makeRDD(seq[(T,seq)]):这种方式可以指定RDD的存放位置
注意:
Spark默认会根据集群的情况来设置分区的数量,也可以通过parallelize()第二参数来指定
Spark会为每一个分区运行一个任务进行处理
文件中的一行文本作为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")
SparkContext.wholeTextFiles():
可以针对一个目录中的大量小文件返回
普通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)))
SparkContext.sequenceFile[K,V]()
Hadoop SequenceFile的读写支持
SparkContext.hadoopRDD()、newAPIHadoopRDD()
从Hadoop接口API创建
SparkContext.objectFile()
RDD.saveAsObjectFile()的逆操作
RDD中操作分为两大类型:转换(transformation)和行动(action)
转换:
通过操作将RDD转换成另外一个RDD;
对于转换操作,RDD的所有转换都不会直接计算结果,仅记录作用于RDD上的操作,当遇到动作算子(Action)时才会进行真正计算
行动:
将一个RDD进行求值或者输出;
本质上动作算子通过SparkContext执行提交作业操作,触发RDD DAG(有向无环图)的执行,所有的动作算子都是急迫型(non-lazy),RDD遇到Action就会立即计算
总结:
所有这些操作主要针对两种类型的RDD:数值RDD和键值对RDD
RDD的所有转换操作都是懒执行的,只有当行动操作出现的时候spark才会真的去运行
RDD的基本讲解就到这里,后面小编会给大家着重说下转换算子和行动算子,记得关注小编!!