一、简介
RDD(弹性分布式数据集)是Spark的核心抽象。它是一组元素,在集群的节点之间进行分区,以便我们可以对其执行各种并行操作。
有两种方法可以用来创建RDD:
- 并行化驱动程序中的现有数据
- 引用外部存储系统中的数据集,例如:共享文件系统,HDFS,HBase或提供Hadoop InputFormat的数据源。
1、并行化集合
并行化集合
要创建并行化集合,请在驱动程序中的现有集合上调用SparkContext的parallelize方法。复制集合的每个元素以形成可以并行操作的分布式数据集。
val info = Array(1, 2, 3, 4)
val distinfo = sc.parallelize(info)
现在,可以操作分布式数据集(distinguishedfo),例如:
distinfo.reduce((a, b) => a + b)。
2、外部数据集
在Spark中,可以从Hadoop支持的任何类型的存储源(如HDFS,Cassandra,HBase甚至本地文件系统)创建分布式数据集。Spark提供对文本文件,SequenceFiles和其他类型的Hadoop InputFormat的支持。
SparkContext的textFile方法可用于创建RDD的文本文件。此方法获取文件的URI(计算机上的本地路径或hdfs://)并读取文件的数据。
现在,可以通过数据集操作来操作数据,例如使用map和reduceoperations来添加所有行的大小,如下所示:
data.map(s => s.length).reduce((a, b) => a + b)。
二、RDD运行过程
1、特性
介绍过程之前先要了解一下RDD的特性
(1)高效的容错性。现有的分布式共享内存、键值存储、内存数据库等,为了实现容错,必须在集群节点之间进行数据复制或者记录日志,也就是在节点之间会发生大量的数据传输,这对于数据密集型应用而言会带来很大的开销。在RDD的设计中,数据只读,不可修改,如果需要修改数据,必须从父RDD转换到子RDD,由此在不同RDD之间建立了血缘关系。所以,RDD是一种天生具有容错机制的特殊集合,不需要通过数据冗余的方式(比如检查点)实现容错,而只需通过RDD父子依赖(血缘)关系重新计算得到丢失的分区来实现容错,无需回滚整个系统,这样就避免了数据复制的高开销,而且重算过程可以在不同节点之间并行进行,实现了高效的容错。此外,RDD提供的转换操作都是一些粗粒度的操作(比如map、filter和join),RDD依赖关系只需要记录这种粗粒度的转换操作,而不需要记录具体的数据和各种细粒度操作的日志(比如对哪个数据项进行了修改),这就大大降低了数据密集型应用中的容错开销;
(2)中间结果持久化到内存。数据在内存中的多个RDD操作之间进行传递,不需要“落地”到磁盘上,避免了不必要的读写磁盘开销;
(3)存放的数据可以是Java对象,避免了不必要的对象序列化和反序列化开销。
2、工作过程
再总结一下RDD在Spark架构中的运行过程(如图所示):
(1)创建RDD对象;
(2)SparkContext负责计算RDD之间的依赖关系,构建DAG;
(3)DAGScheduler负责把DAG图分解成多个阶段,每个阶段中包含了多个任务,每个任务会被任务调度器分发给各个工作节点(Worker Node)上的Executor去执行。
三、RDD操作
RDD提供两种类型的操作:
- 转换
- 行动
之前写过的两篇博客:
1、动作算子
https://blog.csdn.net/qq_42448606/article/details/108734350
2、转换算子
https://blog.csdn.net/qq_42448606/article/details/108751657
四、RDD持久化
Spark通过在操作中将其持久保存在内存中,提供了一种处理数据集的便捷方式。在持久化RDD的同时,每个节点都存储它在内存中计算的任何分区。也可以在该数据集的其他任务中重用它们。
我们可以使用persist()或cache()方法来标记要保留的RDD。Spark的缓存是容错的。在任何情况下,如果RDD的分区丢失,它将使用最初创建它的转换自动重新计算。
存在可用于存储持久RDD的不同存储级别。通过将StorageLevel对象(Scala,Java,Python)传递给persist()来使用这些级别。但是,cache()方法用于默认存储级别,即StorageLevel.MEMORY_ONLY。
以下是存储级别的集合:
存储级别 | 描述 |
---|---|
MEMORY_ONLY | 它将RDD存储为JVM中的反序列化Java对象。这是默认级别。如果RDD不适合内存,则每次需要时都不会缓存和重新计算某些分区。 |
MEMORY_AND_DISK | 它将RDD存储为JVM中的反序列化Java对象。如果RDD不适合内存,请存储不适合磁盘的分区,并在需要时从那里读取它们。 |
MEMORY_ONLY_SER | 它将RDD存储为序列化Java对象(即每个分区一个字节的数组)。这通常比反序列化的对象更节省空间。 |
MEMORY_AND_DISK_SER | 它类似于MEMORY_ONLY_SER ,但是将内存中不适合的分区溢出到磁盘而不是重新计算它们。 |
DISK_ONLY | 它仅将RDD分区存储在磁盘上。 |
MEMORY_ONLY_2 , MEMORY_AND_DISK_2 | 它与上面的级别相同,但复制两个群集节点上的每个分区。 |
OFF_HEAP | 它类似于MEMORY_ONLY_SER ,但将数据存储在堆外内存中。必须启用堆外内存。 |
五、RDD共享变量
在Spark中,当任何函数传递给转换操作时,它将在远程集群节点上执行。它适用于函数中使用的所有变量的不同副本。这些变量将复制到每台计算机,并且远程计算机上的变量更新不会恢复到驱动程序。
spark动作的执行经过几个阶段,由分布式“shuffle”操作分开。Spark自动广播每个阶段中任务所需的公共数据。以这种方式广播的数据以序列化形式缓存并在运行每个任务之前反序列化。
要创建广播变量(比方说,v),请调用SparkContext.broadcast(v)。让我们通过一个例子来理解。
scala> val a=sc.longAccumulator("Accumulator")
scala> sc.parallelize(Array(2,5)).foreach(x=>a.add(x))
scala> a.value
累加器
累加器是用于执行关联和交换操作(例如计数器或总和)的变量。Spark为数字类型的累加器提供支持。但是,可以添加对新类型的支持。
要创建数字累加器,请调用**SparkContext.longAccumulator()或SparkContext.doubleAccumulator()**以累积Long或Double类型的值。
示例
scala> val a=sc.longAccumulator("Accumulator")
scala> sc.parallelize(Array(2,5)).foreach(x=>a.add(x))
scala> a.value
参考文章:
https://www.yiibai.com/spark/apache-spark-rdd-shared-variables.html
http://dblab.xmu.edu.cn/blog/985-2/