1. 概述
本文章将从多个角度介绍Spark中RDD,DataFrame和Dataset的概念以及它们之间的区别。例如,数据表示,不变性和互操作性等,还将说明分别在什么场景下使用RDD,DataFrame API或Dataset的 API。
2. Rdd、Dataframe、Dataset的基本概念
-
RDD
RDD: Resilient Distributed Datasets。它是一个分布式数据集合抽象,是不可变的,支持分区的,懒执行机制的。 RDD是Spark的最基础的数据结构,可以这样理解,它本身并不是一个真正存储数据的集合对象,而是存储了一系列操作的指令集合,这些有序的操作通过action触发,基于血统机制, 在集群上以可容错的方式进行基于内存的分布式的并行计算。 -
DataFrame
与RDD不同,数据被加上对应的schema,类似RDBMS的表结构。它是不可变的分布式数据集合。 Spark中的DataFrame允许开发人员将结构强加到分布式数据集合上,从而实现更高级别的抽象。 -
DataSet
DataSet是DataFrame API的扩展,提供了RDD API的类型安全,面向对象的编程接口的功能以及Catalyst查询优化器的性能优势以及DataFrame API的堆外存储机制。相对于DataFrame它是强类型的,在编译时就可以检查数据类型。
在spark2.0后,DataFrame的API和DataSet的API合并统一了,DataFrame相当于DataSet[Row]。
3. Rdd、Dataframe、Dataset的区别与联系
3.1 发布时间
- RDD:spark 1.0
- DataFrame :spark 1.3
- DataSet :spark 1.6
3.2 数据处理
- RDD:它可以轻松有效地处理结构化和非结构化数据。与Dataframe和DataSet一样,RDD不会推断数据类型,而是要求用户指定。
- DataFrame:它仅适用于结构化和半结构化数据。DataFrame = Rdd + schema
- DataSet:它可以有效地处理结构化和非结构化数据。它以行的JVM对象或行对象的集合的形式表示数据,通过encoders的编码方式以表格形式呈现数据, 相对于rdd它更高效,相对于dataframe它是强类型的。
3.3 支持的数据源
- RDD:数据源API允许RDD可以来自任何数据源,例如文本文件、语音文件、视频文件等等。
- DataFrame:数据源API允许以不同格式(AVRO,CSV,JSON和存储系统HDFS,HIVE表,MySQL)进行数据处理。它可以从上述的各种数据源进行读取和写入。
- DataSet:同rdd一样支持来自不同来源的数据
3.4 不变性和互操作
- RDD:RDD包含已分区数据的集合, RDD中并行性的基本单位称为分区。每个分区都是数据的一个逻辑分区,它是不变的,并且是通过对现有分区进行某种转换而创建的。不变性有助于实现计算的一致性。我们可以通过toDF()方法从RDD移到DataFrame(如果RDD为表格格式),也可以通过.rdd方法进行相反的操作。
- DataFrame:转换为DataFrame后,无法重新生成域对象。即,由RDD[Person] => DataFrame后,DataFrame.rdd得到的是RDD[Row],而不是RDD[Person]
- DataSet:克服了DataFrame的局限性,可以从Dataframe重新生成RDD,DataSet[Person] => RDD[Person]
3.5 编译时安全类型
- RDD:RDD提供了熟悉的面向对象的编程风格以及编译时类型安全性。
- DataFrame:如果您尝试访问表中不存在的列,则Dataframe API不支持编译时检查类型错误,它仅在运行时检测类型错误。
- DataSet:强类型,支持面向对象的编程,也支持Dataframe的类似于SQL的API。
3.6 优化器
- RDD:RDD中没有内置的优化引擎。当使用结构化数据时,RDD无法利用sparks高级优化器的优势。例如,catalyst optimizer and Tungsten execution engine。开发人员需要根据实际情况优化每个RDD。
-
DataFrame:使用catalyst optimizer 进行优化。Dataframe在四个阶段进行优化:
a)Analyzing logical plan 分析逻辑计划。
b)Logical plan optimization 逻辑计划优化。
c)Physical planning 物理计划。
d)Code generation 代码生成,将查询的一部分编译为Java字节码。
下图举例说明了dataframe优化器的作用
下图给出了优化阶段的简要概述:
- DataSet:它包含用于优化查询计划的Dataframe Catalyst优化器的概念。
3.7 序列化
- RDD:每当Spark需要在群集内分发数据或将数据写入磁盘时,它都会使用Java序列化。序列化单个Java和Scala对象的开销非常昂贵,并且需要在节点之间发送数据和结构,因此效率相对较低。
- DataFrame:DataFrame可以将数据以二进制格式序列化到堆外存储(内存中),然后直接在此堆外内存上执行操作。无需使用Java序列化来编码数据。它提供了Tungsten physical execution执行后端,该后端显式管理内存并动态生成字节码以进行表达式求值。
- DataSet:关于数据序列化,Spark中的Dataset API具有encoder 的概念,该encoder 处理JVM对象到表格形式之间的转换。它使用Spark内部的Tungsten二进制格式存储表格表示形式。数据集允许对序列化数据执行操作并改善内存使用率。它允许按需访问单个属性,而无需对整个对象进行反序列化。相对于Java序列化的方式性能提升明显。
3.8 GC处理
- RDD:创建和销毁单个对象会导致垃圾回收的开销,性能会因此降低。
- DataFrame:减少为数据集中的每一行构造单个对象时而产生的垃圾回收成本。
- DataSet:垃圾回收器不需要销毁对象,因为序列化通过 Tungsten执行。采用了堆外数据序列化。
3.9 效率及内存使用
- RDD:在Java和Scala对象上分别执行序列化会花费很多时间,效率会降低。
- DataFrame:使用堆外内存进行序列化可减少开销。它动态生成字节码,因此可以对该序列化数据执行许多操作。小型操作无需反序列化。
- DataSet:它允许对序列化数据执行操作并改善内存使用率。因此,它允许按需访问单个属性,而无需反序列化整个对象。
3.10 Lazy执行机制
- RDD 、DataFrame、DataSet 均采用懒惰机制,运行时首先保存基于数据集的一系列transformation操作,当遇到action操作需要给driver返回结果时,才真正的执行之前保存的那些转换操作。
3.11 编程语言支持
- RDD:RDD API支持Java,Scala,Python和R语言。因此,此功能为开发人员提供了灵活性。
- DataFrame:与RDD一样,同样支持各种语言的API,例如Java,Python,Scala和R。
- DataSet:DataSetAPI当前仅在Scala和Java中可用。 目前不支持Python和R。
3.12 聚合操作
- RDD:RDD API执行简单的分组和聚合操作较慢。
- DataFrame:DataFrame API非常易用。查询分析速度更快,可方便地在大型数据集上进行汇总统计。
- DataSet:DataSet API 对大量数据集执行聚合操作更快。
3.13 应用场景
-
RDD
- 对数据集进行低级转换以及操作和控制,灵活性较强;
- 数据集是非结构化的,例如媒体流或文本流;
- 数据不是结构化的,不关心Dataframe和Dataset带来的性能提升。
-
DataFrame 和 DataSet
Spark2.0后DataFrame和DataSet的API已经完全统一。在以下场景下考虑使用:- 需要实现特殊需求的特定领域的API。请使用DataFrame或DataSet API
- 需要高级表达式,过滤器,映射,聚合,平均值,总和,SQL查询,列访问以及对结构化数据使用lambda函数等,请使用DataFrame或DataSet API
- 需要在编译时获得更高的类型安全性,想要输入类型的JVM对象,利用Catalyst优化并从Tungsten的高效代码生成中受益,请使用DataSet API
- 如果要跨Spark库统一和简化API,请使用DataFrame或Dataset。
如果是R用户,请使用DataFrames。
如果是Python用户,则在需要更多控制权时,请使用DataFrames,RDD作为协助。
总结
通过RDD与DataFrame与Dataset的比较,我们可以根据不同使用场景选择RDD、DataFrame或Dataset。RDD提供了底层功能和控制。 DataFrame和Dataset允许自定义视图和结构。它提供了针对特定领域的高级操作,节省了空间,并且可以高速执行。