RDD(Resilient Distributed Datasets,弹性分布式数据集)代表可并行操作元素的不可变分区集合。对于Spark的初学者来说,这个概念会十分陌生。即便是对于一些有Spark使用经验的人,要想说清楚什么是RDD,以及为什么需要RDD还是一件比较困难的事情。在《深入理解Spark RDD——为什么需要RDD?》一文解释了第二个问题,本文将开启对第一个问题的解答。
有些读者可能对本文的标题感到困惑,这是因为RDD的API非常多,所以本文首先对RDD中与调度系统息息相关的API方法进行分析,转换API、动作API及检查点API将在后续文章中进行介绍。
抽象类RDD定义了所有RDD的规范,我们从RDD的属性开始,逐步了解RDD的实现。
RDD采用了模板方法模式的设计,抽象类RDD中定义了模板方法以及一些未实现的接口,这些接口将需要RDD的各个子类分别实现。下面先来介绍RDD中定义的接口。
@DeveloperApi
def compute(split: Partition, context: TaskContext): Iterator[T]
protected def getPartitions: Array[Partition]
protected def getDependencies: Seq[Dependency[_]] = deps
protected def getPreferredLocations(split: Partition): Seq[String] = Nil
RDD中除定义了以上接口外,还实现了一些模板方法。
partitions方法(见代码清单1)用于获取RDD的分区数组。
代码清单1 partitions的实现
final def partitions: Array[Partition] = {
checkpointRDD.map(_.partitions).getOrElse {
if (partitions_ == null) {
partitions_ = getPartitions
// 省略次要代码
}
partitions_
}
}
根据代码清单1,partitions方法查找分区数组的优先级为:从CheckPoint查找 > 读取partitions_属性 > 调用getPartitions方法获取。检查点的内容将在10.3节详细介绍。
preferredLocations方法(见代码清单2)优先调用CheckPoint中保存的RDD的getPreferredLocations方法获取指定分区的偏好位置,当没有保存CheckPoint时调用自身的getPreferredLocations方法获取指定分区的偏好位置。
代码清单2 preferredLocations的实现
final def preferredLocations(split: Partition): Seq[String] = {
checkpointRDD.map(_.getPreferredLocations(split)).getOrElse {
getPreferredLocations(split)
}
}
dependencies方法(见代码清单3)用于获取当前RDD的所有依赖的序列。
代码清单3 dependencies的实现
final def dependencies: Seq[Dependency[_]] = {
checkpointRDD.map(r => List(new OneToOneDependency(r))).getOrElse {
if (dependencies_ == null) {
dependencies_ = getDependencies
}
dependencies_
}
}
根据代码清单3,dependencies方法的执行步骤如下。
除了以上的模板方法,RDD中还实现的方法如下:
(1)context
context方法(见代码清单4)实际返回了_sc(即SparkContext)。
代码清单4 context的实现
def context: SparkContext = sc
(2)getStorageLevel
getStorageLevel方法(见代码清单5)实际返回了当前RDD的StorageLevel。
代码清单5 获取当前RDD的存储级别
def getStorageLevel: StorageLevel = storageLevel
(3)getNarrowAncestors
getNarrowAncestors方法(见代码清单6)用于获取当前RDD的祖先依赖中属于窄依赖的RDD序列。
代码清单6 getNarrowAncestors的实现
private[spark] def getNarrowAncestors: Seq[RDD[_]] = {
val ancestors = new mutable.HashSet[RDD[_]]
def visit(rdd: RDD[_]) {
val narrowDependencies = rdd.dependencies.filter(_.isInstanceOf[NarrowDependency[_]])
val narrowParents = narrowDependencies.map(_.rdd)
val narrowParentsNotVisited = narrowParents.filterNot(ancestors.contains)
narrowParentsNotVisited.foreach { parent =>
ancestors.add(parent)
visit(parent)
}
}
visit(this)
ancestors.filterNot(_ == this).toSeq
}
如要继续了解RDD,请继续阅读《深入理解Spark RDD——RDD依赖(构建DAG的关键)》。
深入理解Spark RDD系列文章:
《深入理解Spark RDD——为什么需要RDD?》
《深入理解Spark RDD——RDD实现的初次分析》
《深入理解Spark RDD——RDD依赖(构建DAG的关键)》
《深入理解Spark RDD——RDD分区计算器Partitioner》
《深入理解Spark RDD——RDD信息对象》