Spark SQL 是一种即支持SQL又支持命令式数据处理的工具,主要用于处理结构化和半结构化数据,整合SQL查询和Spark编程,通过相同的标准的连接方式连接不同的数据源,同时兼容Hive。其通过对数据的抽象实现对数据的分析处理。包括 DataFrame 和 DataSet 两种,下面详细介绍这两种抽象数据集。
一、DataFrame
以RDD为基础的分布式数据集,类似传统数据库的二维表,带有schema信息。在Spark程序中如果想使用简单的SQL接口对数据进行分析,首先需要将数据源转换成DataFrame对象,进而在对象上调用各种API实现需求。
1、创建DataFrame
可以从内存中读取数据,也可以同文件中读取数据,这里演示从文件中获取。
1.1 定义一个Json的数据文件
user.json内容如下:
{"name": "zhangsan", "age": 25}
{"name": "lisi", "age": 30}
{"name": "wangwu", "age": 25}
1.2 创建环境对象
import org.apache.spark.sql.{DataFrame, SparkSession} val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSQLDemo") val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
1.3 读取json文件获取数据
val dataFrame: DataFrame = spark.read.json("/data/user.json") dataFrame.show()
2、DataFrame基础操作
DataFrome自带了一些对象方法,如select、where、count、filter等。
2.1 查询schema信息
dataFrame.printSchema()
2.2 列选
dataFrame.select("name").show()
2.3 行选
查询年龄大于25的用户
这里需要引入一个隐式转换 import spark.implicits._
dataFrame.select('name, 'age).where('age>25).show()
2.4 添加列
dataFrame.withColumn("gender", lit("")).show()
2.5 修改列名
dataFrame.withColumnRenamed("gender", "sex").show()
2.6 删除列
dataFrame.drop("name").show()
3、SQL语法
通过上面的定义会发现DataFrame是一个底层的数据集对象,并且提供了很多强大的方法。当然Spark同时支持直接执行SQL语句,就像直接操作数据表一样。
首先要注册临时表,然后在临时表上执行SQL 操作。
创建临时表
dataFrame.createOrReplaceTempView("user")
查询临时表
spark.sql("select * from user where name ='zhangsan'").show()
这里需要注意的是,这个临时表只是session范围,即临时的且不能全局方法。如果想全局访问需要创建全局临时表,并使用全局路径访问。即:
dataFrame.createGlobalTempView("user") spark.sql("select * from global_temp.user where name ='zhangsan'").show()
二、DataSet
强类型的数据集合,需要提供对应的类型信息。
从上面的介绍以及前面RDD章节中可以看到RDD 只关注数据本身,定义时不会涉及元数据信息,而 DataFrame 会提供schema信息,但没有强调数据类型。而DataSet 明确指出需要提供类型信息。
1、创建DataSet
1.1 定义一个样式类
case class Person(name: String, age: Long){}
1.2 加载隐式转换
这里是使用专门的编码器对对象进行序列化,转换过程中自动生成基本数据类型。
import spark.implicits._
1.3 定义数据列表并转换成DataSet对象
val list = List(Person("zhangsan", 25), Person("lisi", 29), Person("wangwu", 30)).toDS()
1.4 获取数据
list.select("name", "age").where('age>25).show()
2、DataSet 基础操作
基本操作和DataFrame 类似,这里不再演示。
三、RDD 、DataFrame 、DataSet 三者之间的关系和转换
1、关系
从前面的定义看, DataFrame 和 DataSet 都是基于RDD构建的数据抽象数据集。
共性:
三者都是spark下的分布式弹性数据集。
都是惰性加载,在创建、转换时不会立即执行。
DataFrame 和 DataSet 都需要加载spark.implicits._ , 都可使用模式匹配。
区别:
版本支持:
Spark 1.0 支持RDD;
Spark 1.3 支持 DataFrame;
Spark 1.6支持 DadaSet, 并会逐步替代前两者。
数据结构:
RDD 只包含数据,不关心数据结构。
DataFrame 包含数据、数据结构。
DataSet 包含 数据、数据结构、数据类型。
支持的数据格式:
RDD 支持结构化、非结构化数据。
DataFrame 仅支持 结构化 和半结构化数据。
DataSet 支持结构化和非结构化数据。
其他还有如 序列化方式、垃圾回收等区别,有兴趣可以深入了解。
总结:
三者都是对数据集的抽象,只不过抽象的层次不同,简单理解三者具有包含关系:
DataSet(DataFrame(RDD))
2、转换
定义一个RDD
val rdd: RDD[(Int, String, Int)] = spark.sparkContext.makeRDD(List((1, "zhangsan", 30), (2, "lisi", 29), (3, "wangwu", 32)))
前面提到RDD 包含数据,不包含数据结构和数据类型,DataFrame 包含数据和数据结构但不包含数据类型,所以底层向上层转换时 需要补充 数据结构或 数据类型,这里构建一个样式类。
case class User(id: Int, name: String, age:Int)
2.1 RDD 转 DataFrame
构建数据结构
val df: DataFrame = rdd.toDF("id", "name", "age")
2.2 DataFrame 转 RDD
df.rdd
2.3 RDD 转 DataSet
val ds: Dataset[User] = rdd.map { case (id, name, age) => { User(id, name, age) } }.toDS()
2.4 DataSet 转 RDD
ds.rdd
2.5 DataFrame 转 DataSet
val ds: Dataset[User] = df.as[User]
2.6 DataSet 转 DataFrame
ds1.toDF()
至此Spark SQL的基础讲解完成,后续在实践中慢慢摸索更详细更深层的知识。