Spark中RDD、DataFrame和DataSet的区别与联系
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
一、RDD、DataFrame和DataSet的定义
在开始Spark RDD与DataFrame与Dataset之间的比较之前先让我们看一下Spark中的RDDDataFrame和Datasets的定义
Spark RDDRDD代表弹性分布式数据集。它是记录的只读分区集合。 RDD是Spark的基本数据结构。它允许程序员以容错方式在大型集群上执行内存计算。
Spark Dataframe与RDD不同数据以列的形式组织起来类似于关系数据库中的表。它是一个不可变的分布式数据集合。 Spark中的DataFrame允许开发人员将数据结构(类型)加到分布式数据集合上从而实现更高级别的抽象。
Spark DatasetApache Spark中的Dataset是DataFrame API的扩展它提供了类型安全(type-safe)面向对象(object-oriented)的编程接口。 Dataset利用Catalyst optimizer可以让用户通过类似于sql的表达式对数据进行查询。
1. 细说DataFrame
DataFrame的前身是SchemaRDD。Spark1.3更名为DataFrame。不继承RDD自己实现了RDD的大部分功能。
与RDD类似DataFrame也是一个分布式数据集
1DataFrame可以看做分布式 Row 对象的集合提供了由列组成的详细模式信息使其可以得到优化。DataFrame 不仅有比RDD更多的算子还可以进行执行计划的优化。
2DataFrame更像传统数据库的二维表格除了数据以外还记录数据的结构信息即schema。
3DataFrame也支持嵌套数据类型struct、array和map。
4DataFrame API提供的是一套高层的关系操作比函数式的RDD API要更加友好门槛更低。
5Dataframe的劣势在于在编译期缺少类型安全检查导致运行时出错。
2. 细说DataSet
1DataSet是在Spark1.6中添加的新的接口。
2与RDD相比保存了更多的描述信息概念上等同于关系型数据库中的二维表。
3与DataFrame相比保存了类型信息是强类型的提供了编译时类型检查。
4调用Dataset的方法先会生成逻辑计划然后Spark的优化器进行优化最终生成物理计划然后提交到集群中运行。
5DataSet包含了DataFrame的功能在Spark2.0中两者得到了统一DataFrame表示为DataSet[Row]即DataSet的子集。
3. 结构图解
1RDD[Person]
以Person 为类型参数但不了解其内部结构。
2DataFrame
提供了详细的结构信息schema 列的名称和类型。这样看起来就像一张表了。
3DataSet
不光有schema 信息还有类型信息。
4. 数据图解
假设RDD中的两行数据长这样RDD[Person]
那么DataFrame中的数据长这样
DataFrame = RDD[Row] + SchemaDataFrame 的前身是 SchemaRDD。
那么Dataset中的数据长这样Dataset[Person] = DataFrame + 泛型
或者长这样每行数据是个ObjectDataset[Row]即DataFrame = DataSet[Row]
DataSet包含了DataFrame的功能Spark2.0中两者统一DataFrame表示为DataSet[Row]即DataSet的子集。
5. 补充说明:Row & Schema
Row是一个泛化的无类型 JVM objectRow
对象表示的是一个行Row
的操作类似于 Scala
中的 Map
数据类型。
// 一个对象就是一个对象
val p = People(name = "zhangsan", age = 10)
// 同样一个对象, 还可以通过一个 Row 对象来表示
val row = Row("zhangsan", 10)
// 获取 Row 中的内容
println(row.get(1))
println(row(1))
// 获取时可以指定类型
println(row.getAs[Int](1))
// 同时 Row 也是一个样例类, 可以进行 match
row match {
case Row(name, age) => println(name, age)
}
什么是schema
DataFrame中提供了详细的数据结构信息从而使得SparkSQL可以清楚地知道该数据集中包含哪些列每列的名称和类型各是什么DataFrame中的数据结构信息即为schema。
二、三者的共性
1. RDD、DataFrame、DataSet全都是spark平台下的分布式弹性数据集为处理超大型数据提供便利;
2. 三者都有惰性机制在进行创建、转换如map方法时不会立即执行只有在遇到Action如foreach时三者才会开始遍历运算;
3. 三者有许多共同的函数如filter排序等;
4. 在对DataFrame和Dataset进行操作许多操作都需要这个包:import spark.implicits._在创建好SparkSession对象后尽量直接导入
5. 三者都会根据 Spark 的内存情况自动缓存运算这样即使数据量很大也不用担心会内存溢出
6. 三者都有partition的概念
7. DataFrame和Dataset均可使用模式匹配获取各个字段的值和类型。
DataFrame:
testDF.map{
case Row(col1:String,col2:Int)=>
println(col1);println(col2)
col1
case _=>
""
}
Dataset
case class Coltest(col1:String,col2:Int)extends Serializable //定义字段名和类型
testDS.map{
case Coltest(col1:String,col2:Int)=>
println(col1);println(col2)
col1
case _=>
""
}
三、RDD、DataFrame和DataSet的联系
1. RDD
优点:
- 编译时类型安全
编译时就能检查出类型错误 - 面向对象的编程风格
直接通过类名点的方式来操作数据
缺点:
- 序列化和反序列化的性能开销
无论是集群间的通信, 还是IO操作都需要对对象的结构和数据进行序列化和反序列化 - GC的性能开销
频繁的创建和销毁对象, 势必会增加GC
2. DataFrame
DataFrame引入了schema和off-heap
-
schema : RDD每一行的数据, 结构都是一样的。这个结构就存储在schema中。 Spark通过schame就能够读懂数据, 因此在通信和IO时就只需要序列化和反序列化数据, 而结构的部分就可以省略了。
-
off-heap : 意味着JVM堆以外的内存, 这些内存直接受操作系统管理而不是JVM。Spark能够以二进制的形式序列化数据(不包括结构)到off-heap中, 当要操作数据时, 就直接操作off-heap内存。由于Spark理解schema, 所以知道该如何操作。
off-heap就像地盘, schema就像地图, Spark有地图又有自己地盘了, 就可以自己说了算了, 不再受JVM的限制, 也就不再收GC的困扰了。
通过schema和off-heap, DataFrame解决了RDD的缺点, 但是却丢了RDD的优点。 DataFrame不是类型安全的, API也不是面向对象风格的。
DataFrame也可以叫Dataset[Row],每一行的类型是Row不解析每一行究竟有哪些字段各个字段又是什么类型都无从得知只能用上面提到的getAS方法或者共性中的第七条提到的模式匹配拿出特定字段。
优点:
DataFrame 内部有明确 Scheme 结构即列名、列字段类型都是已知的这带来的好处是可以减少数据读取以及更好地优化执行计划从而保证查询效率。
缺点
(1)Dataframe的劣势在于在编译期缺少类型安全检查导致运行时出错。
(2)DataFrame虽然是结构化的但是其所含的值并没有对应一个class所以spark就定义了一个class名为Row作为DataFrame的数据的数据结构。所以DataFrame等价于Dataset[Row]。但是Row又没有定义field具体包含哪些字段没法直接取出来所以只能通过Row的各种方法比如getAs[Int](xxx)来获取属性xxx的内容。而Dataset每一行是什么类型是不一定的在自定义了case class之后可以很自由的获得每一行的信息。所以DataFrame在获取内部数据的时候方法数据的属性没有Dataset方便。
3. DataSet
DataSet结合了RDD和DataFrame的优点, 并带来的一个新的概念Encoder。
当序列化数据时, Encoder产生字节码与off-heap进行交互, 能够达到按需访问数据的效果, 而不用反序列化整个对象。
四、DataFrame和DataSet的区别
第一点: DataFrame
表达的含义是一个支持函数式操作的 表
, 而 Dataset
表达是是一个类似 RDD
的东西, Dataset
可以处理任何对象。
第二点: DataFrame
中所存放的是 Row
对象, 而 Dataset
中可以存放任何类型的对象。
val spark: SparkSession = new sql.SparkSession.Builder()
.appName("hello")
.master("local[6]")
.getOrCreate()
import spark.implicits._
val df: DataFrame = Seq(People("zhangsan", 15), People("lisi", 15)).toDF()
val ds: Dataset[People] = Seq(People("zhangsan", 15), People("lisi", 15)).toDS()
DataFrame 就是 Dataset[Row]
Dataset 的范型可以是任意类型
第三点: DataFrame
的操作方式和 Dataset
是一样的, 但是对于强类型操作而言, 它们处理的类型不同。
DataFrame
在进行强类型操作时候, 例如 map
算子, 其所处理的数据类型永远是 Row
df.map( (row: Row) => Row(row.get(0), row.getAs[Int](1) * 10) )(RowEncoder.apply(df.schema)).show()
但是对于 Dataset
来讲, 其中是什么类型, 它就处理什么类型
ds.map( (item: People) => People(item.name, item.age * 10) ).show()
第四点: DataFrame
只能做到运行时类型检查, Dataset
能做到编译和运行时都有类型检查。
-
DataFrame
中存放的数据以Row
表示, 一个Row
代表一行数据, 这和关系型数据库类似 -
DataFrame
在进行map
等操作的时候,DataFrame
不能直接使用Person
这样的Scala
对象, 所以无法做到编译时检查 -
Dataset
表示的具体的某一类对象, 例如Person
, 所以再进行map
等操作的时候, 传入的是具体的某个Scala
对象, 如果调用错了方法, 编译时就会被检查出来。
val ds: Dataset[People] = Seq(People("zhangsan", 15), People("lisi", 15)).toDS()
//这行代码明显报错, 无法通过编译
ds.map(person => person.hello)