- RDD最基本的五个特征
RDD 即 Resilient Distributes Dataset, 是spark中最基础、最常用的数据结构。其本质是把input source 进行封装,封装之后的数据结构就是RDD,提供了一系列操作,比如 map、flatMap、filter等。input source种类繁多,比如hdfs上存储的文件、本地存储的文件,相应的 RDD的种类也有很多。不同的input source 对应着不同的rdd 类型。比如从hdfs上读取的text对应着HadoopRDD, val hRdd= sc.textFile(“hfs://…….”) 生成的就是HadoopRDD。
如图所示,RDD是一个父类,是一个抽象类,封装了rdd的基本属性和常用操作。每一个具体的子类rdd都继承了该父类。
rdd包含五个特征:
- 一个分片列表 partition list
- 一个计算函数compute,对每一个split进行计算
- 对其他rdd的依赖列表dependencies list.依赖又份 宽依赖和窄依赖。
- partitioner for key-value RDDs.比如说 hash-partitioned rdd(这是可选的,并不是所有的add都会有这个特征)
- 对每一个split计算的优先位置 Preferred Location。比如对一个hdfs文件进行计算时,可以获取优先计算的block locations
以上五个特征中包含四个函数和一个属性,如下所示:
- protected def getPartitions: Array[Partition] //only called once
- def compute(split: Partition, context: TaskContext): Iterator[T]
- protected def getDependencies: Seq[Dependency[_]] = dips //only called once
- protected def getPreferredLocations(split: Partition): Seq[String] = Nil
- @transient val partitioner: Option[Partitioner] = None
这五个特征的定义包含在抽象父类RDD中(对应于上图中的RDD,具体的代码在RDD.scala中),每一个具体类型的rdd子类继承RDD父类之后都会部分或全部override这五个特征。如MapPartitionsRDD就重写了其中的三个:
- override val partitioner = if (preservesPartitioning) firstParent[T].partitioner else None
- override def getPartitions: Array[Partition] = firstParent[T].partitions
- override def compute(split: Partition, context: TaskContext): Iterator[U] = f(context, split.index, firstParent[T].iterator(split, context))
- RDD structure
抽象父类RDD的结构如下:
<注: 图中列出的是主要属性和方法>
如上图所示,每一个rdd都有一个id,这个id是调用sparkcontext中的函数生成的:
/** A unique ID for this RDD (within its SparkContext). */ val id: Int = sc.newRddId()
而newRddId()最终调用的是AtmoicInteger.java中类AtomicInteger的getAndIncrement()方法。在常用的方法中,persist()是一个持久化函数,调用该函数要设置
storageLevel,常见的设置有MEMORY_ONLY(这是默认值),MEMOEY_AND_DISK,以及DISK_ONLY。checkpoint()方法会把当前rdd保存到一个目录中,该目录是调用sparkcontext中的
setCheckpointDir()方法生成的。checkpoint的时候,会把所有依赖的父级rdd信息清除掉。注意,checkpoint不会马上执行,要触发action操作的时候才会执行。
因为 checkpoint会清除父级rdd的信息,所以在做checkpoint应该先做persist操作,否则就要重新计算一遍。checkpoint的好处显而易见,比如做1000次迭代,在
第999次时做了checkpoint,如果第1000次的时候,只要重新计算第1000即可,不用从头到尾再计算一次。
rdd中有两种算子:
- Transformation: 返回一个新rdd,可以对返回的rdd继续做transformation操作。因为一个rdd的不同分区可以存在不同节点上,所以transformation是可以
并行的,一个task对应一个rdd的块。另外,transformation 操作是lazy的,不会马上执行,只有触发了Action算子时才会执行。
- Action: 返回值不是rdd,返回值是一个集合、值或者返回为空。返回的结果要么传给driver,要么保存到文件系统。
以下是常有的算子: