阅读本篇文章,你预期可以得到下面几个问题的答案:
- Rdd DataSet DataFrame的区别
- Row类型是什么
- Row格式是怎么存储的
1、Rdd
RDD(Resilient Distributed Dataset)弹性分布式数据集,是spark框架中最基本的抽象元素。具有不可变,可伸缩、易并行的特点;
它具有几个比较重要的属性:
- 一系列分片;就是partition的概念,主要是为了实现并行
- 对于每个分片都会参与到一次计算中
- RDD存在相互依赖的关系,这也是spark的DAG以及算子依赖的思想
- 自定义的分区函数;目前spark已经实现的,常用的hash和range;
- 自定义的分区函数;目前spark已经实现的,常用的hash和range
优点:
- 存储的是java对象,类型转换安全
- 通用的,对于结构化和非结构化的数据都可以处理;
缺点:
- 存储的java对象,在传输过程中需要进行序列化,默认的序列化速度慢;
- java对象,意味着会有频繁的GC;
- 比较原始,不存在谓词下推等优化手段;
2、DataFrame
dataframe可以简单理解为一张二维表,拥有表头(schema)的概念,可以很方便的让spark运行过程中知道数据的组织形式;同时开发人员也可以基于schema进行数据的转换和操作;
相比Rdd:
2.1、相同点
- 都是分布式数据集;
- 都是不可变的数据集;
- 都拥有很丰富的算子操作;
2.2、不同点
- rdd存储类型为java对象,dataframe存储类型为dataset[Row],有schema的定义,开发者可以通过列名获取对应列的数据;
- rdd执行是按照定义的算子进行计算,而dataframe是拥有比rdd更多的操作算子,同时有一些更深层次的优化:例如谓词下推;
3、Dataset
Dataset是强类型、特定域的、能并行执行的的数据集合;
而每一个Dataset都有一个特定的实例:DataFrame,它是Dataset[Row]类型
相比DataFrame:
3.1、DataFrame vs Dataset
和前面说的一致:每一个Dataset都有一个特定的实例:DataFrame,它是Dataset[Row]类型
可以理解为:DataFrame是Dataset的一种类型下的表现形式。如果你是用scala开发spark的程序,那对于类型这块的感知不是那么的明显,如果你是使用java开发spark的程序的话你会注意到一个现象:使用spark.sql()返回的明明是一个DataFrame,但是对象的类型却只能定义为Dataset[Row],是不是还有过怀疑呢??从定义来看,人家DataFrame只是Dataset在18岁的容颜而已。
3.2、Dataset结构
Dataset和rdd一样,也有几个相同的属性:
- 惰性执行
- 有两类算子:transform和action
那么为了spark为了支持Dataset定义的这种特定域的情况下,spark到底是怎么实现的呢?
每一个Dataset都有一个encoder编码器,它是把泛型T转换成spark内部系统可以识别的基本类型,比如int,string等;比如一个Dataset[Person]类型,在spark生成代码并序列化的时候,encoder的作用就是告诉spark,将Person里面的属性:sex,age作为基本类型序列化成二进制结构,当然带来的好处就是较少的内存占用和更高效的数据处理速度;
4、rdd DataFrame转换
对于一个rdd转化为DataFrame的操作非常简单:
val df = rdd.toDF()
val df = rdd.toDF("id","name")
那么他具体的实现是什么呢?需要研究一下toDF函数
- 判断rdd的tuple的元素的个数和输入的colNames是不是一一对应的;
- 进行列和列名的匹配;
logicalPlan.output.zip(colNames)
匹配是通过scala集合直接的zip拉链操作实现,所以在toDF函数输入的列名顺序要和rdd元素的顺序完全一致,这里需要特别小心。
5、Row
前面知道DataFrame是Dataset[Row]的格式,那么就需要了解一下spark的Row到底是什么,以什么格式存储数据的;
我们先直观想一下,Row,中文含义:行;
哦,那他肯定就是行存储的,意思是将同一行的数据汇聚到一起作为一个Row;恭喜你,确实如此,Row就是一行数据,Row有两个基本的操作:
- 构建:
Row()和Row.fromSeq
两种方式 - 获取数据:获取数据是通过索引号来获取,实现的方法有很多个:getString(i)等
下面是spark支持的类型和java基本类型的对应关系:
既然是行,那存储方式就是数组了呀;