Spark2.0 RDD DataFrame DataSet 如何选择?

RDD和Dataset最大的不同在于,RDD是low-level的API和内核,Dataset实际上基于底层的引擎构建的high-level的计算引擎。

1、RDD使用场景

1、如果我们需要对数据集进行非常底层的掌控和操作,比如说,手动管理RDD的分区,或者根据RDD的运行逻辑来结合各种参数和编程来进行较为底层的调优。因为实际上Dataframe/Dataset底层会基于whole-stage code generation技术自动生成很多代码,那么就意味着,当我们在进行线上报错的troubleshooting以及性能调优时,对Spark的掌控能力就会降低。而使用Spark Core/RDD,因为其运行完全遵循其源码,因此我们完全可以在透彻阅读Spark Core源码的基础之上,对其进行troubleshooting和底层调优。(最重要的一点)
2、我们要处理的数据是非结构化的,比如说多媒体数据,或者是普通文本数据。
3、我们想要使用过程式编程风格来处理数据,而不想使用domain-specific language的编程风格来处理数据。
4、我们不关心数据的schema,即元数据。
5、我们不需要Dataframe/Dataset底层基于的第二代tungsten引擎提供的whole-stage code generation等性能优化技术。

2、DataSet

从spark2.0开始 DataSet 有两种表现方式 typed APIuntyped API
DataFrame就是DataSet[Row]的别名,Row就是一个untyped类型的对象
因为Row是类似于数据库中的一行,只知道有哪些列,即使列不存在,也可以对这些不存在的列进行操作。被定义为untyped,就是弱类型
Dataset本身,是一种typed类型的API,其中的Object通常是我们自己自定义的typed类型对象,因为对象是自己定义的,所以包括字段命名
以及字段类型都是强类型的。Scala支持Dataset和DataFrame两种类型,java仅仅支持Dataset类型

Dataset API有点?

1、静态类型以及运行时的类型安全性

SQL语言具有最不严格的限制,而Dataset具有最严格的限制。SQL语言在只有在运行时才能发现一些错误,比如类型错误,但是由于Dataframe/Dataset目前都是要求类型指定的(静态类型),因此在编译时就可以发现类型错误,并提供运行时的类型安全。比如说,如果我们调用了一个不属于Dataframe的API,编译时就会报错。但是如果你使用了一个不存在的列,那么也只能到运行时才能发现了。而最严格的就是Dataset了,因为Dataset是完全基于typed API来设计的,类型都是严格而且强类型的,因此如果你使用了错误的类型,或者对不存在的列进行了操作,都能在编译时就发现。
Spark2.0 RDD DataFrame DataSet 如何选择?_第1张图片
2、将半结构化的数据转换为typed自定义类型

举例来说,如果我们现在有一份包含了学校中所有学生的信息,是以JSON字符串格式定义的,比如:{“name”: “leo”, “age”, 19, “classNo”: 1}。我们可以自己定义一个类型,比如case class Student(name: String, age: Integer, classNo: Integer)。接着我们就可以加载指定的json文件,并将其转换为typed类型的Dataset[Student],比如val ds = spark.read.json(“students.json”).as[Student]。

在这里,Spark会执行三个操作:
1、Spark首先会读取json文件,并且自动推断其schema,然后根据schema创建一个Dataframe。
2、在这里,会创建一个Dataframe=Dataset[Row],使用Row来存放你的数据,因为此时还不知道具体确切的类型。
3、接着将Dataframe转换为Dataset[Student],因为此时已经知道具体的类型是Student了。

这样,我们就可以将半结构化的数据,转换为自定义的typed结构化强类型数据集。并基于此,得到之前说的编译时和运行时的类型安全保障。

3、API的易用性

Dataframe/Dataset引入了很多的high-level API,并提供了domain-specific language风格的编程接口。这样的话,大部分的计算操作,都可以通过Dataset的high-level API来完成。通过typed类型的Dataset,我们可以轻松地执行agg、select、sum、avg、map、filter、groupBy等操作。使用domain-specific language也能够轻松地实现很多计算操作,比如类似RDD算子风格的map()、filter()等。

4、性能

除了上述的优点,Dataframe/Dataset在性能上也有很大的提升。首先,Dataframe/Dataset是构建在Spark SQL引擎之上的,它会根据你执行的操作,使用Spark SQL引擎的Catalyst来生成优化后的逻辑执行计划和物理执行计划,可以大幅度节省内存或磁盘的空间占用的开销(相对于RDD来说,Dataframe/Dataset的空间开销仅为1/3~1/4),也能提升计算的性能。其次,Spark 2.x还引入第二代Tungsten引擎,底层还会使用whole-stage code generation、vectorization等技术来优化性能。

何时使用DataSet?

1、如果需要更加丰富的计算语义,high-level的抽象语义,以及domain-specific API。
2、如果计算逻辑需要high-level的expression、filter、map、aggregation、average、sum、SQL、列式存储、lambda表达式等语义,来处理半结构化,或结构化的数据。
3、如果需要高度的编译时以及运行时的类型安全保障。
4、如果想要通过Spark SQL的Catalyst和Spark 2.x的第二代Tungsten引擎来提升性能。
5、如果想要通过统一的API来进行离线、流式、机器学习等计算操作。
6、如果是R或Python的用户,那么只能使用Dataframe。

SparkSession 入口

从Spark 2.0开始,一个最大的改变就是,Spark SQL的统一入口就是SparkSession,SQLContext和HiveContext未来会被淘汰。可以通过SparkSession.builder()来创建一个SparkSession,如下代码所示。SparkSession内置就支持Hive,包括使用HiveQL语句查询Hive中的数据,使用Hive的UDF函数,以及从Hive表中读取数据等。

val spark = SparkSession
  .builder()
  .appName("Spark SQL Example")
  .master("local") 
  .config("spark.sql.warehouse.dir", "C:\\Users\\Administrator\\Desktop\\spark-warehouse")  
  .getOrCreate()

import spark.implicits._

Dataframe:untyped操作

有了SparkSession之后,就可以通过已有的RDD,Hive表,或者其他数据源来创建Dataframe,比如说通过json文件来创建。Dataframe提供了一种domain-specific language来进行结构化数据的操作,这种操作也被称之为untyped操作,与之相反的是基于强类型的typed操作。

val df = spark.read.json("people.json")
df.show()
df.printSchema()
df.select("name").show()
df.select($"name", $"age" + 1).show()
df.filter($"age" > 21).show()
df.groupBy("age").count().show()

Dataset:typed操作

Dataset与RDD比较类似,但是非常重要的一点不同是,RDD的序列化机制是基于Java序列化机制或者是Kryo的,而Dataset的序列化机制基于一种特殊的Encoder,来将对象进行高效序列化,以进行高性能处理或者是通过网络进行传输。Dataset除了Encoder,也同时支持Java序列化机制,但是encoder的特点在于动态的代码生成,同时提供一种特殊的数据格式,来让spark不将对象进行反序列化,即可直接基于二进制数据执行一些常见的操作,比如filter、sort、hash等。

case class Person(name: String, age: Long)
val caseClassDS = Seq(Person("Andy", 32)).toDS()
caseClassDS.show()

val primitiveDS = Seq(1, 2, 3).toDS()
primitiveDS.map(_ + 1).collect()

val path = "people.json"
val peopleDS = spark.read.json(path).as[Person]
peopleDS.show()

操作hive

case class Record(key: Int, value: String)
val warehouseLocation = "file:${system:user.dir}/spark-warehouse"

val spark = SparkSession
  .builder()
  .appName("Spark Hive Example")
  .config("spark.sql.warehouse.dir", warehouseLocation)
  .enableHiveSupport()
  .getOrCreate()

import spark.implicits._
import spark.sql

你可能感兴趣的:(实战)