Spark 中最基本的数据抽象是 RDD。数组
RDD:弹性分布式数据集 (Resilient Distributed DataSet)。缓存
这三个特性分别为:分区,不可变,并行操做。分布式
每个 RDD 包含的数据被存储在系统的不一样节点上。逻辑上咱们能够将 RDD 理解成一个大的数组,数组中的每一个元素就表明一个分区 (Partition) 。函数
在物理存储中,每一个分区指向一个存储在内存或者硬盘中的数据块 (Block) ,其实这个数据块就是每一个 task 计算出的数据块,它们能够分布在不一样的节点上。线程
因此,RDD 只是抽象意义的数据集合,分区内部并不会存储具体的数据,只会存储它在该 RDD 中的 index,经过该 RDD 的 ID 和分区的 index 能够惟一肯定对应数据块的编号,而后经过底层存储层的接口提取到数据进行处理。3d
在集群中,各个节点上的数据块会尽量的存储在内存中,只有当内存没有空间时才会放入硬盘存储,这样能够最大化的减小硬盘 IO 的开销。orm
不可变性是指每一个 RDD 都是只读的,它所包含的分区信息是不可变的。因为已有的 RDD 是不可变的,因此咱们只有对现有的 RDD 进行转化 (Transformation) 操做,才能获得新的 RDD ,一步一步的计算出咱们想要的结果。cdn
这样会带来这样的好处:咱们在 RDD 的计算过程当中,不须要马上去存储计算出的数据自己,咱们只要记录每一个 RDD 是通过哪些转化操做得来的,即:依赖关系,这样一方面能够提升计算效率,一方面是错误恢复会更加容易。若是在计算过程当中,第 N 步输出的 RDD 的节点发生故障,数据丢失,那么能够根据依赖关系从第 N-1 步去从新计算出该 RDD,这也是 RDD 叫作**"弹性"**分布式数据集的一个缘由。对象
由于 RDD 的分区特性,因此其自然支持并行处理的特性。即不一样节点上的数据能够分别被处理,而后生成一个新的 RDD。blog
每一个 RDD 里都会包括分区信息、依赖关系等等的信息,以下图所示:
Partitions 就是上面所说的,表明着 RDD 中数据的逻辑结构,每一个 Partion 会映射到某个节点内存或者硬盘的一个数据块。
SparkContext 是全部 Spark 功能的入口,表明了与 Spark 节点的链接,能够用来建立 RDD 对象以及在节点中的广播变量等等。一个线程只有一个 SparkContext。
SparkConf 是一些配置信息。
Partitioner 决定了 RDD 的分区方式,目前两种主流的分区方式:Hash partioner 和 Range partitioner。Hash 就是对数据的 Key 进行散列分布,Rang 是按照 Key 的排序进行的分区。也能够自定义 Partitioner。
Dependencies 也就是依赖关系,记录了该 RDD 的计算过程,也就是说这个 RDD 是经过哪一个 RDD 通过怎么样的转化操做获得的。
这里有个概念,根据每一个 RDD 的分区计算后生成的新的 RDD 的分区的对应关系,能够分红窄依赖和宽依赖。
窄依赖就是父 RDD 的分区能够一一对应到子 RDD 的分区,宽依赖是说父 RDD 的每一个分区能够被多个子 RDD 分区使用。如图:
因为窄依赖的特性,窄依赖容许子 RDD 的每一个分区能够被并行处理产生,并且支持在同一个节点上链式执行多条指令,无需等待其它父 RDD 的分区操做。
Spark 区分宽窄依赖的缘由主要有两点:
检查点机制,在计算过程当中有一些比较耗时的 RDD,咱们能够将它缓存到硬盘或者 HDFS 中,标记这个 RDD 有被检查点处理过,而且清空它的全部依赖关系。同时,给它新建一个依赖于 CheckpointRDD 的依赖关系,CheckpintRDD 能够用来从 硬盘中读取 RDD 和生成新的分区信息。
这么作以后,当某个 RDD 须要错误恢复时,回溯到该 RDD,发现它被检查点记录过,就能够直接去硬盘读取该 RDD,无需从新计算。
针对每个分片,都会选择一个最优的位置来计算,数据不动,代码动。
用来记录 RDD 持久化时存储的级别,经常使用的有:
迭代函数和计算函数是用来表示 RDD 怎样经过父 RDD 计算获得的。
迭代函数首先会判断缓存中是否有想要计算的 RDD,若是有就直接读取,若是没有就查找想要计算的 RDD 是否被检查点处理过。若是有,就直接读取,若是没有,就调用计算函数向上递归,查找父 RDD 进行计算。