Spark学习总结(二)—— Spark Sql

1、SparkSQL

 SparkSQL是spark用于结构化数据处理的spark模块

 Hive 是早期唯一运行在 Hadoop 上的 SQL-on-Hadoop 工具。

 但是 MapReduce 计算过程中大量的中间磁盘落地过程消耗了大量的 I/O,降低的运行效率,为了提高 SQL- on-Hadoop的效率,大量的 SQL-on-Hadoop 工具开始产生

 其中 Shark 是伯克利实验室 Spark 生态环境的组件之一,是基于 Hive 所开发的工具,它修

 改了下图所示的右下角的内存管理、物理计划、执行三个模块,并使之能运行在 Spark 引擎上。

 但是,随着 Spark 的发展,对于野心勃勃的 Spark 团队来说,Shark 对于 Hive 的太多依

赖(如采用 Hive 的语法解析器、查询优化器等等),制约了 Spark 的 One Stack Rule Them All的既定方针, 制约了 Spark 各个组件的相互集成,所以提出了 SparkSQL 项目

 SparkSQL 主要提供了2个编程抽象,类似于RDD,它们分别为DataFrame和DataSet

 

2、特点

  1. 易整合

    无缝的整合了SQL查询和Spark编程

  1. 统一的数据访问

    使用相同的方式连接不同的数据源

  1. 兼容Hive

    在已有的仓库上直接运行SQL或者HiveQL

  1. 标准数据连接

    通过JDBC或者ODBC来连接

 

3、DataFrame

 DataFrame 是一种以 RDD 为基础的分布式数据集,类似于传统数据库中的二维表格。DataFrame 与 RDD 的主要区别在于,前者带有 schema 元信息,即 DataFrame所表示的二维表数据集的每一列都带有名称和类型。

 DataFrame 是为数据提供了 Schema 的视图。可以把它当做数据库中的一张表来对待

 DataFrame 也是懒执行的,但性能上比 RDD 要高,主要原因:优化的执行计划,即查询计划通过 Spark catalyst optimiser 进行优化。

 

4、DataSet

 DataSet 是分布式数据集合。DataSet 是 Spark 1.6 中添加的一个新抽象,是 DataFrame的一个扩展。它提供了 RDD 的优势(强类型,使用强大的 lambda 函数的能力)以及 Spark SQL 优化执行引擎的优点。

DataSet 也可以使用功能性的转换(操作 map,flatMap,filter等等)。

 它可以把表中的一行当做一个对象来操作,就可以避免查询时的属性顺序问题

 

5、DataFrame核心编程

 在 Spark SQL 中 SparkSession 是创建 DataFrame 和执行 SQL 的入口,创建 DataFrame

有三种方式:

  (1)通过 Spark 的数据源进行创建;

    如果从内存中获取数据,spark 可以知道数据类型具体是什么。如果是数字,默认作为 Int 处理;但是从文件中读取的数字,不能确定是什么类型,所以用 bigint 接收,可以和Long 类型转换,但是和 Int 不能进行转换

scala> val df = spark.read.json("data/user.json")

df: org.apache.spark.sql.DataFrame = [age: bigint, username: string]

  (2)从一个存在的 RDD 进行转换;

    SQL 语法风格是指我们查询数据的时候使用 SQL 语句来查询,这种风格的查询必须要有临时视图或者全局视图来辅助

scala> df.createOrReplaceTempView("people")

scala> val sqlDF = spark.sql("SELECT * FROM people")

sqlDF: org.apache.spark.sql.DataFrame = [age: bigint, name: string]

 

 注意,普通临时表是 Session 范围内的,如果想应用范围内有效,可以使用全局临时表。使用全局临时表时需要全路径访问,例如global_temp.people

  (3)从 Hive Table 进行查询返回;

    注意:涉及到运算的时候, 每列都必须使用$, 或者采用引号表达式:单引号+字段名

scala> df.select($"username",$"age" + 1).show scala> df.select('username, 'age + 1).show()

 

6、RDD之间的转换

将RDD中的数据转换成DataFrame模式

//List(1,2,3,4) -> table(id : 1,2,3,4)

scala> idRDD.toDF("id").show

//table(id : 1,2,3,4) -> List(1,2,3,4)

val rdd = df.rdd

 

注意:此时得到的 RDD 存储类型为 Row

 

7、DataSet核心编程

 DataSet 是具有强类型的数据集合,需要提供对应的类型信息。它包含了DataFrame中的属性以及RDD中的数据,相当于将DataFrame封装成了一个类

 

8、DataFrame和DataSet转换

//DataFrame 转换 DataSet

//构造类

scala> case class User(name:String, age:Int) defined class User

scala> val df = sc.makeRDD(List(("zhangsan",30), ("lisi",49))).toDF("name","age") df: org.apache.spark.sql.DataFrame = [name: string, age: int]

scala> val ds = df.as[User]

ds: org.apache.spark.sql.Dataset[User] = [name: string, age: int]

//DataSet 转 换 DataFrame scala> val ds = df.as[User]

ds: org.apache.spark.sql.Dataset[User] = [name: string, age: int]

scala> val df = ds.toDF

df: org.apache.spark.sql.DataFrame = [name: string, age: int]

 

9、RDD和DataSet转换

//RDD 转换 DataSet

scala> case class User(name:String, age:Int) defined class User

scala> sc.makeRDD(List(("zhangsan",30), ("lisi",49))).map(t=>User(t._1, t._2)).toDS res11: org.apache.spark.sql.Dataset[User] = [name: string, age: int]

注意:转换的前提是预先将RDD中的数据用转换类(User)进行封装,这样转换后才能使用方便
//DataSet 转 换 RDD scala> val rdd = res11.rdd

rdd: org.apache.spark.rdd.RDD[User] = MapPartitionsRDD[51] at rdd at <console>:25

scala> rdd.collect

res12: Array[User] = Array(User(zhangsan,30), User(lisi,49))
 

10、RDD & DataSet & DataFrame三者关系

三者的共性

 (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

 【1】RDD 一般和 spark mllib 同时使用

 【2】RDD 不支持 sparksql 操作

(2)DataFrame

 【1】与 RDD 和 Dataset 不同,DataFrame 每一行的类型固定为 Row,每一列的值没法直接访问,只有通过解析才能获取各个字段的值

 【2】DataFrame 与 DataSet 一般不与 spark mllib 同时使用

 【3】DataFrame 与 DataSet 均支持 SparkSQL 的操作,比如 select,groupby 之类,还能注册临时表/视窗,进行 sql 语句操作

 【4】DataFrame 与 DataSet 支持一些特别方便的保存方式,比如保存成 csv,可以带上表头,这样每一列的字段名一目了然(后面专门讲解)

(3)DataSet

 【1】Dataset 和 DataFrame 拥有完全相同的成员函数,区别只是每一行的数据类型不同。DataFrame 其实就是 DataSet 的一个特例 type DataFrame = Dataset[Row]

 【2】DataFrame 也可以叫 Dataset[Row],每一行的类型是 Row,不解析,每一行究竟有哪些字段,各个字段又是什么类型都无从得知,只能用上面提到的 getAS 方法或者共性中的第七条提到的模式匹配拿出特定字段。而 Dataset 中,每一行是什么类型是不一定的,在自定义了 case class 之后可以很自由的获得每一行的信息

 

11、用户自定义函数

(1)UDF

  用户可以通过spark.udf功能添加自定义的函数来实现自定义的功能

【1】创建DataFrame

scala> val df = spark.read.json("data/user.json")

 

【2】注册UDF

scala> spark.udf.register("addName",(x:String)=> "Name:"+x)

 

【3】创建临时表

scala> df.createOrReplaceTempView("people")

 

【4】应用UDF

scala> spark.sql("Select addName(name),age from people").show()

 

(2)UDAF

 强类型的 Dataset 和弱类型的 DataFrame 都提供了相关的聚合函数, 如 count(),countDistinct(),avg(),

max(),min()。除此之外,用户可以设定自己的自定义聚合函数。通过继承UserDefinedAggregateFunction 来重写实现用户自定义弱类型聚合函数。从 Spark3.0 版本后,UserDefinedAggregateFunction 已经不推荐使用了。可以统一采用强类型聚合函数Aggregator

 

12、数据的加载和保存

(1)通用的加载和保存方式

 SparkSQL 提供了通用的保存数据和数据加载的方式。这里的通用指的是使用相同的API,根据不同的参数读取和保存不同格式的数据,SparkSQL 默认读取和保存的文件格式为 parquet

【1】加载数据:spark.read.load

  • format("…"):指定加载的数据类型,括"csv"、"jdbc"、"json"、"orc"、"parquet"和

"textFile"。

  • load("…"):在"csv"、"jdbc"、"json"、"orc"、"parquet"和"textFile"格式下需要传入加载数据的路径。
  • option("…"):在"jdbc"格式下需要传入 JDBC 相应参数,url、user、password 和 dbtable

【2】保存数据:df.write.save

  • format("…"):指定保存的数据类型,包括"csv"、"jdbc"、"json"、"orc"、"parquet"和"textFile"。
  • save ("…"):在"csv"、"orc"、"parquet"和"textFile"格式下需要传入保存数据的路径。
  • option("…"):在"jdbc"格式下需要传入 JDBC 相应参数,url、user、password 和 dbtable

保存操作可以使用 SaveMode, 用来指明如何处理数据,使用 mode()方法来设置。有一点很重要: 这些 SaveMode 都是没有加锁的, 也不是原子操作。

 

(2) Parquet

 Spark SQL 的默认数据源为 Parquet 格式。Parquet 是一种能够有效存储嵌套数据的列式存储格式。

 数据源为 Parquet 文件时,Spark SQL 可以方便的执行所有的操作,不需要使用 format。修改配置项spark.sql.sources.default,可修改默认数据源格式。

【1】加载数据

scala> val df = spark.read.load("examples/src/main/resources/users.parquet")

 

【2】保存数据

var df = spark.read.json("/opt/module/data/input/people.json")

//保存为 parquet 格式

scala> df.write.mode("append").save("/opt/module/data/output")

 

posted @ 2023-07-27 17:45  sftsgly  阅读(76)  评论(0编辑  收藏  举报