RDD,全称为Resilient Distributed Datasets,是一个容错的、并行的数据结构,可以让用户显式地将数据存储到磁盘和内存中,并能控制数据的分区。同时,RDD还提供了一组丰富的操作来操作这些数据。在这些操作中,诸如map、flatMap、filter等转换操作实现了monad模式,很好地契合了Scala的集合操作。除此之外,RDD还提供了诸如join、groupBy、reduceByKey等更为方便的操作(注意,reduceByKey是action,而非transformation),以支持常见的数据运算。
Spark 中的RDD 就是一个不可变的分布式对象集合。每个RDD 都被分为多个分区,这些
分区运行在集群中的不同节点上。RDD 可以包含Python、Java、Scala 中任意类型的对象,
甚至可以包含用户自定义的对象。
用户可以使用两种方法创建RDD:1.读取一个外部数据集,或在驱动器程序里分发驱动器程
序中的对象集合(比如list 和set)。2.由一个RDD 生成一个新的RDD
RDD 支持两种类型的操作: 转化操作(transformation) 和行动操作 (action)。
转化操作会由一个RDD 生成一个新的RDD。
行动操作会对RDD 计算出一个结果,并把结果返回到驱动器程序中,或把结
果存储到外部存储系统(如HDFS)
转化操作和行动操作的区别在于Spark 计算RDD 的方式不同。虽然你可以在任何时候定
义新的RDD,但Spark 只会惰性计算这些RDD。它们只有第一次在一个行动操作中用到
时,才会真正计算。
Represents an immutable, partitioned collection of elements that can be operated on in parallel:表示不可变的,集合中的分区可以并行运行(rdd一旦生产不可变,可分区,并行计算的特点)
https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/rdd/RDD.scala
RDD的五个主要特点:
A list of partitions 分区
A function for computing each split 一个函数应用于各个分区(并行计算)
A list of dependencies on other RDDs 依赖其他RDD 传递依赖 RDD1=>RDD2=>RDD3
Optionally(可选), a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)
Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file)
数据在哪个节点,task在哪个节点运行性能最好,不用在各个节点上进行数据传输
abstract class RDD[T: ClassTag](
@transient private var _sc: SparkContext,
@transient private var deps: Seq[Dependency[_]]
) extends Serializable with Logging {
}
说明rdd是抽象类,必然是子类实现
可序列化
T 泛型 支持各种数据类型
RDD的特点与源码的对应关系如下:
def compute(split: Partition, context: TaskContext): Iterator[T]
(分区,每个task一个上下文) RDD的 第二个特点
在SPARK中,计算时,有多少个patition就对应多少个task来执行
protected def getPartitions: Array[Partition]
RDD第一个特点
protected def getDependencies: Seq[Dependency[_]] = deps
获取依赖关系 第三个特点
protected def getPreferredLocations(split: Partition): Seq[String] = Nil
第五个特点
RDD的操作大致分为4类
创建操作 如makeRDD
转换操作 如map
控制操作 如cache
行动操作 如collect
RDD的实现
作业调度
当对RDD执行转换操作时,调度器会根据RDD的血统来构建由若干调度阶段(Stage)组成的有向无环图(DAG),每个调度阶段包含尽可能多的连续窄依赖转换。调度器按照DAG顺序计算,并最终得到目标RDD。
调度器向各个节点分配任务采用延时调度机制并根据数据存储位置(数据本地性)来确定。若一个任务需要处理的某个分区刚好存储在某个节点的内存中,则该任务分配给该节点;如果在内存中不包含该分区,调度器会找到包含该RDD的较佳的位置,并把任务分配给所在节点。
spark如何计算作业调度调度阶段如下图所示:
对于执行失败的任务,只要它的对应调度阶段父类信息依然可以用,该任务会分散到其他阶段重新执行,如果某些调度阶段不可用,则重新提交相应的任务,并以并行方式计算丢失的分区。在作业中如果某个任务执行缓慢,系统则会在其他节点上执行该任务的副本,并取最先得到的结果作为最终结果.
解析器集成
内存管理
spark提供了三种持久化RDD的存储策略:未序列化java对象存在内存中,序列化的数据存于内存以及存储在磁盘中
第一个选项是最优的,因为可以直接访问在java虚拟机内存中的RDD对象;
空间有限的情况下,第二种方式可以让用户采用比java对象更有效的内存组织方式,但代价是降低了效率;
第三种策略适用于RDD太大的情形,每次重新计算改RDD会带来额外的资源开销(I/O)
对于内存使用LRU回收算法来进行管理,当计算得到一个新的RDD分区,但没有足够的空间存储时,系统会从最近最少使用的RDD回收其一个分区的空间。除非该RDD是新分区对应的RDD,这种情况下,spark会将旧的分区继续保留在内存中,防止同一个RDD的分区被循环调入/调出。
检查点支持
虽然血统可以用于错误后的RDD恢复,但对于很长血统的RDD,这样恢复会耗时比较长,因此需要通过检查点操作(checkpoint)来保存到外部存储中。
多用户管理
spark通用接口
spark的四类操作:创建,转换,控制,行为