目录
基本介绍
DataFrame 和 DataSet 模型是Spark SQL 所提供的两个编程抽象,SparkSQL核心编程主要是关于使用DataFrame 和 DataSet 模型进行编程以及它们之间的关系和转换。
Spark Core 中,如果想要执行应用程序,需要首先构建上下文环境对象 SparkContext,Spark SQL 其实可以理解为对 Spark Core 的一种封装,不仅仅在模型上进行了封装,上下文环境对象也进行了封装。
在老的版本中,SparkSQL 提供两种 SQL 查询起始点:
- SQLContext,用于 Spark自己提供的 SQL 查询
- HiveContext,用于连接 Hive 的查询。
SparkSession 是 Spark 最新的 SQL 查询起始点,实质上是 SQLContext 和 HiveContext的组合,所以在 SQLContex 和 HiveContext 上可用的 API 在 SparkSession 上同样是可以使用的。SparkSession 内部封装了 SparkContext,所以计算实际上是由 sparkContext 完成的。
DataFrame
Spark SQL 的 DataFrame API 允许直接使用 DataFrame 而不用必须去注册临时表或者生成 SQL 表达式。DataFrame API 既有 transformation 操作也有 action 操作。
创建 DataFrame
在 Spark SQL 中 SparkSession 是创建 DataFrame 和执行 SQL 的入口,创建 DataFrame有三种方式:
- 通过 Spark 的数据源进行创建;
- 从一个存在的 RDD 进行转换;
- 从 HiveTable 进行查询返回。
(1)通过 Spark 的数据源进行创建
①进入Spark
[xzc@hadoop102 spark-local]$ bin/spark-shell
②查看Spark支持创建文件的数据源格式
scala> spark.read.
③在 spark 的 bin/data 目录中创建 user.json 文件
{
"username": "zhangsan","age":20}
{
"username": "lisi", "age":30}
{
"username": "wangwu", "age":40}
④读取 json 文件创建 DataFrame
scala> val df = spark.read.json("data/user.json")
⑤展示结果
scala> df.show
(2)从RDD转换
在 IDEA 中开发程序时,如果需要 RDD 与 DF 或者 DS 之间互相操作,那么需要引入 以下代码:
import spark.implicits._
这里的 spark 不是 Scala 中的包名,而是创建的 sparkSession 对象的变量名称,所以必须先创建 SparkSession 对象再导入。这里的 spark 对象不能使用 var 声明,因为 Scala 只支持val 修饰的对象的引入。 而在spark-shell 中无需导入,自动完成此操作。
①在 spark 的 bin/data 目录中创建id.txt文档
②读取txt文件
scala> val idRDD = sc.textFile("data/id.txt")
③转换为DataFrame并展示结果
scala> idRDD.toDF("id").show
注:实际开发中,一般通过样例类将 RDD 转换为 DataFrame
scala> case class User(name:String, age:Int)
scala> sc.makeRDD(List(("zhangsan",30), ("lisi",40))).map(t=>User(t._1, t._2)).toDF.show
(3)从Hive Table查询返回
Apache Hive 是 Hadoop 上的 SQL 引擎,Spark SQL 编译时可以包含 Hive 支持,也可以不包含。包含 Hive 支持的 Spark SQL 可以支持 Hive 表访问、UDF (用户自定义函数)以及 Hive 查询语(HiveQL/HQL)等。需要强调的一点是,如果要在 Spark SQL 中包含Hive 的库,并不需要事先安装 Hive。若要把 Spark SQL 连接到一个部署好的 Hive 上,你必须把 hive-site.xml 复制到 Spark 的配置文件目录中($SPARK_HOME/conf)。即使没有部署好 Hive,Spark SQL 也可以运行。
hive-site.xml文件下载,需要根据自己的配置修改个别内容
注意:如果你没有部署好 Hive,Spark SQL 会在当前的工作目录中创建出自己的 Hive 元数据仓库,叫作metastore_db。此外,如果你尝试使用 HiveQL 中的 CREATE TABLE (并非 CREATE EXTERNAL TABLE)语句来创建表,这些表会被放在你默认的文件系统中的 /user/hive/warehouse 目录中(如果你的 classpath 中有配好的 hdfs-site.xml,默认的文件系统就是 HDFS,否则就是本地文件系统)。
spark-shell 默认是 Hive 支持的;代码中是默认不支持的,需要手动指定(加一个参数即可)。
1)内嵌的 HIVE
如果使用 Spark 内嵌的 Hive, 则什么都不用做, 直接使用即可.
Hive 的元数据存储在 derby 中, 默认仓库地址:$SPARK_HOME/spark-warehouse
①展示tables
scala> spark.sql("show tables").show
②创建一个表
scala> spark.sql("create table aa(id int)")
③向表中加载本地数据并展示
scala> spark.sql("load data local inpath 'data/id.txt' into table aa")
scala> spark.sql("select * from aa").show
注:在实际使用中, 几乎没有任何人会使用内置的 Hive
2)外部的 HIVE
如果想连接外部已经部署好的 Hive,需要通过以下几个步骤:
- Spark 要接管 Hive 需要把 hive-site.xml 拷贝到 conf/目录下
- 把 Mysql 的驱动 copy 到 jars/目录下
- 如果访问不到 hdfs,则需要把 core-site.xml 和 hdfs-site.xml 拷贝到 conf/目录下
- 重启 spark-shell
DataSet
DataSet 是具有强类型的数据集合,需要提供对应的类型信息。
创建 DataSet
创建 DataFrame有两种方式:
- 使用样例类序列创建 DataSet
- 使用基本类型的序列创建 DataSet
(1)使用样例类序列创建 DataSet
①创建Person样例类
scala> case class Person(name: String, age: Long)
②向表中添加数据并展示
scala> val caseClassDS = Seq(Person("zhangsan",2)).toDS()
scala> caseClassDS.show
(2)使用基本类型的序列创建 DataSet
①创建DataSet
scala> val ds = Seq(1,2,3,4,5).toDS
②展示数据
scala> ds.show
注:在实际使用的时候,很少用到把序列转换成 DataSet,更多的是通过RDD 来得到 DataSet
RDD 转换为 DataSet
SparkSQL 能够自动将包含有 case 类的 RDD 转换成 DataSet,case 类定义了 table 的结构,case 类属性通过反射变成了表的列名。Case 类可以包含诸如 Seq 或者 Array 等复杂的结构。
scala> case class User(name:String, age:Int)
scala> sc.makeRDD(List(("zhangsan",30), ("lisi",49))).map(t=>User(t._1, t._2)).toDS
DataSet 转换为 RDD
DataSet 其实也是对 RDD 的封装,所以可以直接获取内部的 RDD
scala> case class User(name:String, age:Int)
scala> sc.makeRDD(List(("zhangsan",30), ("lisi",49))).map(t=>User(t._1, t._2)).toDS
scala> val rdd = res11.rdd
scala> rdd.collect
DataFrame 和 DataSet 转换
DataFrame 其实是 DataSet 的特例,所以它们之间是可以互相转换的。
(1)DataFrame 转换为 DataSet
scala> case class User(name:String, age:Int)
scala> val df = sc.makeRDD(List(("zhangsan",30), ("lisi",49))).toDF("name","age")
scala> val ds = df.as[User]
(2)DataSet 转换为 DataFrame
scala> val ds = df.as[User]
scala> val df = ds.toDF
RDD、DataFrame、DataSet 三者的关系
在 SparkSQL 中 Spark 为我们提供了两个新的抽象,分别是 DataFrame 和 DataSet。他们和 RDD 有什么区别呢?首先从版本的产生上来看:
- Spark1.0 => RDD
- Spark1.3 => DataFrame
- Spark1.6 => Dataset
如果同样的数据都给到这三个数据结构,他们分别计算之后,都会给出相同的结果。不同是的他们的执行效率和执行方式。在后期的 Spark 版本中,DataSet 有可能会逐步取代 RDD和 DataFrame 成为唯一的 API 接口。
三者的共性
(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 均可使用模式匹配获取各个字段的值和类型
三者的区别
(1) RDD
- RDD 一般和 spark mllib 同时使用
- RDD 不支持 sparksql 操作
(2) DataFrame
- 与 RDD 和 Dataset 不同,DataFrame 每一行的类型固定为 Row,每一列的值没法直接访问,只有通过解析才能获取各个字段的值
- DataFrame 与 DataSet 一般不与 spark mllib 同时使用
- DataFrame 与 DataSet 均支持 SparkSQL 的操作,比如 select,groupby 之类,还能注册临时表/视窗,进行 sql 语句操作
- DataFrame 与 DataSet 支持一些特别方便的保存方式,比如保存成 csv,可以带上表头,这样每一列的字段名一目了然(后面专门讲解)
(3)DataSet
- Dataset 和 DataFrame 拥有完全相同的成员函数,区别只是每一行的数据类型不同。DataFrame 其实就是 DataSet 的一个特例 type DataFrame = Dataset[Row]
- DataFrame 也可以叫 Dataset[Row],每一行的类型是 Row,不解析,每一行究竟有哪些字段,各个字段又是什么类型都无从得知,只能用上面提到的 getAS 方法或者共性中的第七条提到的模式匹配拿出特定字段。而 Dataset 中,每一行是什么类型是不一定的,在自定义了 case class 之后可以很自由的获得每一行的信息