RDD源码: https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/rdd/RDD.scala
1.什么是RDD:
RDD(Resilient Distributed Dataset)弹性分布式数据集
- 弹性:在分布式计算的时候可以容错,比如某个节点挂了,或者某个部分的数据丢失了,可以通过RDD的机制进行修复
- 分布式:数据归属在大的集群上,可以拆成多份存储在各个节点上运行,这样才能体现分布式计算框架的价值所在,因为他们之间并没有依赖关系
- 数据集:可以通过读取一个文件或者通过编程的方式来创建一个数据集,也可以通过一个数据的转换来得到一个新的数据集(后面会详细讲RDD的三种创建方式),这个数据集可以被拆分成多个分区来进行计算;类似HDFS上以block为单位,拆分成多个block进行存储。
RDD是Spark中最基本的数据抽象单元,可以大大降低开发者开发分布式应用程序的门槛并提高执行效率;spark core是基于RDD进行编程的;spark streaming 是基于DStream进行编程的;spark SQL是借助于HiveContext来编程的;
RDD代表着一种不可变的集合,集合里的所有元素可以被分区,这些被拆分成的每一部分可以通过并行的方式进行操作。
例如:
RDDA:(1,2,3,4,5,6,7,8,9)
hadoop000:Partition1:(1,2,3)
hadoop001:Partition2:(4,5,6)
hadoop002:Partition3:(7,8,9)
RDDA被拆分成了三个分区,RDD是由多个Partition所构成的,对RDD中的每个元素进行操作实际上就是对hadoop000、hadoop001、hadoop002三台机器上的Partition1、Partition2、Partition3进行操作
2.RDD的定义:
abstract class RDD[T: ClassTag](
@transient private var _sc: SparkContext,
@transient private var deps: Seq[Dependency[_]]
) extends Serializable with Logging {
解释:
1)抽象类(说明不能直接使用),我们直接使用其子类即可
2)可序列化Serializable ,可存储各类数据
3)Logging 记录日志
4)T 泛型,支持各种数据类型(Spark程序的编译以及运行是区分了Driver和Executor的,只有在运行的时候才知道完整的信息)
5)SparkContext
6)@transient
3.RDD的五大特性:
1) A list of partitions
RDD是由分区组成的,有一系列Partition构成
2) A function for computing each split
对于计算,一个函数是作用于每个分片上的,并行计算
3) A list of dependencies on other RDDs
依赖关系 (加载)RDDA==>RDDB==>RDDC==>RDDD,RDDD是会依赖于前面的,从RDDA加载进来,然后转换变成了RDDB,那么RDDA里的每个Partition就变成了RDDB里的Partition,他们之间会有依赖关系,如果RDDA里的某个与元素或者分区挂了,那重新加载一下就可以了;当然这里还会提到一,就是如果是窄依赖的话,如果RDDB里的某个元素或者分区挂了,那也可以用RDDA重新转换一下,同样的RDD中任一分区数据丢失了,Spark可以通过这个依赖关系重新计算分区里的数据,而不需要计算所有分区,这也体现了一个弹性,后面会对宽依赖以及窄依赖会详细介绍
4) Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)
(a list of preferred locations:数据最好在哪台机器上运行),可对key-value的RDDS做分区,默认是hash分区,当然也可以指定一个Partition及逆行分区,常用的有hash,range
5)Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file)
可选,在对每个分区进行计算的时候,可以选择在哪台机器上执行、计算最好;数据本地性,这样可以提升性能,因为没有移动的数据
spark中的所有调度和执行都是基于这些方法完成的,允许每个RDD实现自己的计算方式,实际上,用户可以通过重写这些函数来实现自定义的RDD。
4.五大特征在源码中的体现:
1)def compute(split: Partition, context: TaskContext): Iterator[T]
对RDD做计算就是对分区做计算(体现第二点)
2)protected def getPartitions: Array[Partition]
一个数组、一个集合类型是Partition(体现的是第一点)
3) protected def getDependencies: Seq[Dependency[_]] = deps()
RDD之间有对应关系,有转换,有依赖(体现第三点)
4)protected def getPreferredLocations(split: Partition): Seq[String] = Nil
传进来split,返回的是一个数组,返回最合适的路径(体现第五大特点)
5)@transient val partitioner: Option[Partitioner] = None
对应KV,指定如何分区(体现第四大特点)
5.图解RDD:
解释:
假定现在有个RDD,它有五个partition,分布在三个节点之上。
RDD是可以做cache操作的,它有可能存在磁盘上,也有可能存在内存里,也有可能两个都有,也有可能存在多个副本。
现在有五个partition,如果取做某个计算时,就必然有五个task。如果core够用的话,就必然是五个task并行的去计算,如果不够的话,就先跑一轮,跑完再继续。
对于partition1来说,你去执行一个计算,肯定优先在NODE1节点上执行性能更好,如果在NODE2节点上执行,需要先把partition1的数据传输到NODE2上,再进行执行计算。
在Spark中,计算时,有多少partition就对应多少个task来执行