SparkSQL 基础编程

一、介绍

Spark Core 中,如果想要执行应用程序,需要首先构建上下文环境对象 SparkContext,Spark SQL 其实可以理解为对Spark Core 的一种封装,不仅仅在模型上进行了封装,上下文环境对象也进行了封装。

在老的版本中,SparkSQL 提供两种 SQL 查询起始点:一个叫 SQLContext,用于 Spark 自己提供的 SQL 查询;一个叫HiveContext,用于连接 Hive 的查询。

SparkSession 是 Spark 最新的 SQL 查询起始点,实质上是 SQLContext 和 HiveContext 的组合,所以在 SQLContext 和HiveContext 上可用的 API 在 SparkSession 上同样是可以使用的。SparkSession 内部封装了 SparkContext,所以计算实际上是由 SparkContext 完成的。当我们使用 spark-shell 的时候,spark 会自动的创建一个叫做 spark 的 SparkSession,就像我们以前可以自动获取到一个 sc 来表示 SparkContext。

二、DataFrame

2.1 dataframe 介绍

在 Spark 中,DataFrame 是一种以 RDD 为基础的分布式数据集,类似于传统数据库中的二维表格。DataFrame 与 RDD 的主要区别在于,前者带有 schema 元信息,即 DataFrame 所表示的二维表数据集的每一列都带有名称和类型。这使得 Spark SQL 得以洞察更多的结构信息,从而对藏于 DataFrame 背后的数据源以及作用于 DataFrame 之上的变换进行了针对性的优化,最终达到大幅提升运行时效率的目标。反观 RDD,由于无从得知所存数据元素的具体内部结构,Spark Core 只能在 stage 层面进行简单、通用的流水线优化。同时,与 Hive 类似,DataFrame 也支持嵌套数据类型(struct、array 和 map)。从 API 易用性的角度上看,DataFrame API 提供的是一套高层的关系操作,比函数式的 RDD API 要更加友好,门槛更低。

2.2 dataframe 使用

1、创建 dataframe
(1) 从 Spark 数据源进行创建
A、查看 Spark 支持创建文件的数据源格式

scala> spark.read.
csv      jdbc   load     options   parquet   table   textFile   
format   json   option   orc       schema    text               

B、创建 user.json 文件

[root@hadoop151 data]# cat user.json 
{"username":"zhangsan","age":20}

C、读取 json 文件创建 dataframe

scala> var df = spark.read.json("/opt/module/spark/data/user.json")
df: org.apache.spark.sql.DataFrame = [age: bigint, username: string]         
+---+--------+                                                                  
|age|username|
+---+--------+
| 20|zhangsan|
+---+--------+

(2) 从 RDD 进行转换

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

2、SQL 语法
SQL 语法风格是指我们查询数据的时候使用 SQL 语句来查询,这种风格的查询必须有临时视图或者全局视图来辅助。
A、读取 JSON 文件创建 DataFrame

scala> val df = spark.read.json("/opt/module/spark/data/user.json")
df: org.apache.spark.sql.DataFrame = [age: bigint, username: string] 

B、对 DataFrame 创建一个临时表

scala> df.createOrReplaceTempView("people")

C、通过 SQL 语句实现查询全表

scala> val sqlDF = spark.sql("select * from people")

D、结果展示

scala> sqlDF.show
+---+--------+                                                                  
|age|username|
+---+--------+
| 20|zhangsan|
+---+--------+

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

E、对于 DataFrame 创建一个全局表

df.createGlobalTempView("people")

F、通过 SQL 语句实现查询全表

spark.sql("SELECT * FROM global_temp.people").show()

三、DataSet

3.1 dataset 介绍

DataSet 是分布式数据集合。DataSet 是 Spark 1.6 中添加的一个新抽象,是 DataFrame 的一个扩展。它提供了 RDD 的优势(强类型,使用强大的 lambda 函数的能力)以及 Spark SQL 优化执行引擎的优点。DataSet 也可以使用功能性的转换(操作 map、flatMap、filter等等)。
1、DataSet 是 DataFrame API 的一个扩展,是 SparkSQL 最新的数据抽象。
2、用户友好的 API 风格,既具有类型安全检查也具有 DataFrame 的查询优化特性。
3、用样例类来对 DataSet 中定义数据的结构信息,样例类中每个属性的名称直接映射到 DataSet 中的字段名称。
4、DataSet 是强类型的,比如可以有 DataSet[Car]、DataSet[Person]。
5、DataFrame 是 DataSet 的特例,DataFrame=DataSet[Row],所以可以通过 as 方法将 DataFrame 转换为 DataSet。Row 是一个类型,跟 Car、Person 这些的类型一样,所有的表结构信息都用 Row 来表示。获取数据时需要指定顺序。

3.2 dataset 使用

1、使用样例类序列创建 DataSet


scala> case class Person(name: String, age: Long)
defined class Person

scala> val caseClassDS = Seq(Person("zhangsan",2)).toDS()
caseClassDS: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]

scala> caseClassDS.show
+--------+---+
|    name|age|
+--------+---+
|zhangsan|  2|
+--------+---+

2、使用基本类型的序列创建 DataSet

scala>  val ds = Seq(1,2,3,4,5).toDS
ds: org.apache.spark.sql.Dataset[Int] = [value: int]

scala>  ds.show
+-----+
|value|
+-----+
|    1|
|    2|
|    3|
|    4|
|    5|
+-----+

四、RDD、DataFrame、DataSet 三者的关系

4.1 介绍

在 SparkSQL 中 Spark 为我们提供了两个新的抽象,分别是 DataFrame 和 DataSet。他们和 RDD 有什么区别呢?首先从版本的产生上来看:
Spark1.0 => RDD
Spark1.3 => DataFrame
Spark1.6 => Dataset
如果同样的数据都给到这三个数据结构,他们分别计算之后,都会给出相同的结果。不同是的他们的执行效率和执行方式。在后期的 Spark 版本中,DataSet 有可能会逐步取代 RDD 和 DataFrame 成为唯一的 API 接口。

4.2 共性

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 均可使用模式匹配获取各个字段的值和类型。

4.3 区别

1、RDD
(1) RDD 一般和 spark mlib 同时使用
(2) RDD 不支持 sparksql 操作

2、DataFrame
(1) 与 RDD 和 Dataset 不同,DataFrame 每一行的类型固定为 Row,每一列的值没法直接访问,只有通过解析才能获取各个字段的值。
(2) DataFrame 与 DataSet 一般不与 spark mlib 同时使用。
(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 之后可以很自由的获得每一行的信息。

4.4 相互转换

在 IDEA 中开发程序时,如果需要 RDD 与 DF 或者 DS 之间互相操作,那么需要引入 import spark.implicits._ 。
这里的 spark 不是 Scala 中的包名,而是创建的 sparkSession 对象的变量名称,所以必须先创建 SparkSession 对象再导入。这里的 spark 对象不能使用 var 声明,因为 Scala 只支持 val 修饰的对象的引入。spark-shell 中无需导入,自动完成此操作。

1、rdd 与 dataframe
(1) rdd -> dataframe
一般通过样例类来转换

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

scala> sc.makeRDD(List(("zhangsan",30), ("lisi",40))).map(t => User(t._1,t._2)).toDF.show
+--------+---+                                                                  
|    name|age|
+--------+---+
|zhangsan| 30|
|    lisi| 40|
+--------+---+

(2) dataframe -> rdd

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

scala> val rdd = df.rdd
rdd: org.apache.spark.rdd.RDD[org.apache.spark.sql.Row] = MapPartitionsRDD[12] at rdd at <console>:25

scala> val array = rdd.collect
array: Array[org.apache.spark.sql.Row] = Array([zhangsan,30], [lisi,40])        

scala> array(0)
res1: org.apache.spark.sql.Row = [zhangsan,30]

scala> array(0)(0)
res2: Any = zhangsan

scala> array(0).getAs[String]("name")
res3: String = zhangsan

2、rdd 与 dataset
(1) 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
res4: org.apache.spark.sql.Dataset[User] = [name: string, age: int]

(2) dataset -> rdd

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

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

scala> val rdd = ds.rdd
rdd: org.apache.spark.rdd.RDD[User] = MapPartitionsRDD[19] at rdd at <console>:25

scala> rdd.collect
res5: Array[User] = Array(User(zhangsan,30), User(lisi,49))

3、dataframe 与 dataset
(1) 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]

(2) 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]

你可能感兴趣的:(spark,数据库,大数据,java,spark,spark-sql)