题记
通过本篇的梳理,预计大家可以对spark的RDD有更加深入的理解,而不是只为了面试做一个概念的理解。。。
RDD基本概念
1、定义
对于这个定义,网络一搜一大把,这里借用一下。
RDD(Resilient Distributed Dataset)弹性分布式数据集,是spark框架中最基本的抽象元素。具有不可变,可伸缩、易并行的特点。
注意到RDD的组成了吗?有一个Dataset 哦?那是不是spark里面的dataset的概念呢?大家可以思考一下
当然,对于定义了解以后,就需要这个它都包含什么属性?
2、属性
先看一下源码
简单理解一下:一个RDD都包含如下的属性:
- 一系列分片;就是partition的概念,主要是为了实现并行
- 对于每个分片都会参与到一次计算中
- RDD存在相互依赖的关系,这也是spark的DAG以及算子依赖的思想
- 自定义的分区函数;目前spark已经实现的,常用的hash和range;
- 存储每个partition中数据文件的位置
看完rdd的定义,你下次说rdd的时候就不会再仅仅说一个“弹性分布式数据集”!!!
3、主要实现函数(RDD子类必须实现)
对于每个partition里面的数据进行计算,并返回这个partition的迭代器
获取rdd的partition的个数
获取rdd的所有依赖
获取partition的偏好位置,例如hdfs block位置
4、rdd的具象
如果仔细查看rdd的源码,
我们看到,RDD这个类是抽象类,而且它的类型是ClassTag类型的泛型,那这里其实可以引出两个问题:
- RDD的类型是程序定义的,spark并不知道RDD的数据类型,这就是RDD与DataFrame的区别之一;
- 在使用rdd时我们都以为类型就是rdd,其实在底层是实现了RDD类的各种类型的具象RDD类;
如下图所示,继承了rdd的类有:
这里继承了RDD的有70+,这里不一一读源码了,挑选几个简单的,常用的做个抛砖引玉吧。。。
3.1、MapPartitionsRDD
这里教大家一个小技巧:不管看什么代码,先看这块代码的输入和输出,确定输入和输出以后再确定这个代码实现了什么功能,然后再去看怎么实现的
- 参数
prev:输入的父rdd;
f:对于每个partition里面执行的函数
preservesPartitioning:是否保留partition的原来状态,一般来说经过转化,父rdd都会转换为另一种格式的子rdd,这时候为了节约资源就不需要保留父rdd的partition信息了;但是如果父rdd是pairrdd并且transform算子没有对key做修改的话,这里父子partition是一样的,就像是父亲和你共用一个姓,不同名字而已
isFromBarrier:说明这个rdd的来源,是不是从一个stage中
isOrderSensitive:f函数是不是对于输入的顺序比较敏感 - 实现函数
重写partitioner函数
获取partition的个数
f函数的具体计算逻辑
这几个函数大家自己看看