SparkSql
一、Spark SQL概述
1、什么是 Spark SQL
Spark SQL 是 Spark 用于结构化数据(structured data)处理的 Spark 模块.
与基本的 Spark RDD API 不同, Spark SQL 的抽象数据类型为 Spark 提供了关于数据结构和正在执行的计算的更多信息.
在内部, Spark SQL 使用这些额外的信息去做一些额外的优化.
有多种方式与 Spark SQL 进行交互, 比如: SQL 和 Dataset API. 当计算结果的时候, 使用的是相同的执行引擎, 不依赖你正在使用哪种 API 或者语言.
这种统一也就意味着开发者可以很容易在不同的 API 之间进行切换, 这些 API 提供了最自然的方式来表达给定的转换.
我们已经学习了 Hive,它是将 Hive SQL 转换成 MapReduce 然后提交到集群上执行,大大简化了编写 MapReduc 的程序的复杂性,
由于 MapReduce 这种计算模型执行效率比较慢, 所以 Spark SQL 的应运而生,它是将 Spark SQL 转换成 RDD,然后提交到集群执行,执行效率非常快!
Spark SQL 它提供了2个编程抽象, 类似 Spark Core 中的 RDD
- DataFrame
- DataSet
2、Spark SQL 的特点
-
Integrated(易整合)
无缝的整合了 SQL 查询和 Spark 编程.

-
Uniform Data Access(统一的数据访问方式)
使用相同的方式连接不同的数据源.

-
Hive Integration(集成 Hive)
在已有的仓库上直接运行 SQL 或者 HiveQL

-
Standard Connectivity(标准的连接方式)
通过 JDBC 或者 ODBC 来连接

3、DataFrame
与 RDD 类似,DataFrame 也是一个分布式数据容器。然而DataFrame更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即schema。同时,与Hive类似,DataFrame也支持嵌套数据类型(struct、array和map)。从 API 易用性的角度上看,DataFrame API提供的是一套高层的关系操作,比函数式的 RDD API 要更加友好,门槛更低。

上图直观地体现了DataFrame和RDD的区别。
左侧的RDD[Person]虽然以Person为类型参数,但Spark框架本身不了解Person类的内部结构。
而右侧的DataFrame却提供了详细的结构信息,使得 Spark SQL 可以清楚地知道该数据集中包含哪些列,每列的名称和类型各是什么。
DataFrame是为数据提供了Schema的视图。可以把它当做数据库中的一张表来对待, DataFrame也是懒执行的,性能上比 RDD要高,主要原因: 优化的执行计划:查询计划通过Spark catalyst optimiser进行优化。
4、DataSet
- 是DataFrame API的一个扩展,是 SparkSQL 最新的数据抽象(1.6新增)。
- 用户友好的API风格,既具有类型安全检查也具有DataFrame的查询优化特性。
- Dataset支持编解码器,当需要访问非堆上的数据时可以避免反序列化整个对象,提高了效率。
- 样例类被用来在DataSet中定义数据的结构信息,样例类中每个属性的名称直接映射到DataSet中的字段名称。
- DataFrame是DataSet的特列,DataFrame=DataSet[Row] ,所以可以通过as方法将DataFrame转换为DataSet。Row是一个类型,跟Car、Person这些的类型一样,所有的表结构信息都用Row来表示。
- DataSet是强类型的。比如可以有DataSet[Car],DataSet[Person].
- DataFrame只是知道字段,但是不知道字段的类型,所以在执行这些操作的时候是没办法在编译的时候检查是否类型失败的,比如你可以对一个String进行减法操作,在执行的时候才报错,而DataSet不仅仅知道字段,而且知道字段类型,所以有更严格的错误检查。就跟JSON对象和类对象之间的类比。
二、Spark SQL 编程
1、SparkSession
在老的版本中,SparkSQL 提供两种 SQL 查询起始点:一个叫SQLContext,用于Spark 自己提供的 SQL 查询;一个叫 HiveContext,用于连接 Hive 的查询。
从2.0开始, SparkSession是 Spark 最新的 SQL 查询起始点,实质上是SQLContext和HiveContext的组合,所以在SQLContext和HiveContext上可用的 API 在SparkSession上同样是可以使用的
SparkSession内部封装了SparkContext,所以计算实际上是由SparkContext完成的。
当我们使用 spark-shell 的时候, spark 会自动的创建一个叫做spark的SparkSession, 就像我们以前可以自动获取到一个sc来表示SparkContext
2、DataFrame
Spark SQL 的 DataFrame API 允许我们使用 DataFrame 而不用必须去注册临时表或者生成 SQL 表达式.
DataFrame API 既有 transformation操作也有action操作. DataFrame的转换从本质上来说更具有关系, 而 DataSet API 提供了更加函数式的 API
DataFrame 简单演示

在bin目录的input文件中创建json文件



2.1、创建 DataFrame
-
通过 Spark 的数据源创建
查看数据源:spark.read.

-
通过已知的 RDD 来创建
-
通过查询一个 Hive 表来创建.
如果从内存中获取数据,spark 可以知道数据类型具体是什么。如果是数字,默认作为 Int 处理;但是从文件中读取的数字,不能确定是什么类型,所以用 bigint 接收,可以和Long 类型转换,但是和 Int 不能进行转换
2.2、SQL 语法
(1) 读取 JSON 文件创建 DataFrame
scala> val df = spark.read.json("bin/input/user.json")

(2) 对 DataFrame 创建一个临时表
scala> df.createOrReplaceTempView("user")

(3) 通过 SQL 语句实现查询全表
scala> val sqlDF = spark.sql("SELECT * FROM user")
sqlDF: org.apache.spark.sql.DataFrame = [age: bigint, name: string]

注意:普通临时表是 Session 范围内的,如果想应用范围内有效,可以使用全局临时表。使用全局临时表时需要全路径访问,如:global_temp.people
(4) 对于 DataFrame 创建一个全局表
scala> df.createGlobalTempView("emp")
(5) 通过 SQL 语句实现查询全表
scala> spark.sql("SELECT * FROM global_temp.emp").show()
+---+--------+
|age|username|
+---+--------+
| 30|zhangsan|
| 20| lisi|
| 40| wangwu|
+---+--------+
scala> spark.newSession().sql("SELECT * FROM global_temp.emp").show()
+---+--------+
|age|username|
+---+--------+
| 30|zhangsan|
| 20| lisi|
| 40| wangwu|
+---+--------+
2.3、DSL语法
DataFrame 提供一个特定领域语言(domain-specific language, DSL)去管理结构化的数据。可以在Scala, Java, Python 和 R 中使用 DSL,使用 DSL 语法风格不必去创建临时视图了
(1)创建一个 DataFrame
scala> val df = spark.read.json("bin/input/user.json")
df: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
(2)查看 DataFrame 的 Schema 信息
scala> df.printSchema
root
|-- age: Long (nullable = true)
|-- username: string (nullable = true)

(3)只查看"age"列数据
scala> df.select("age").show
+---+
|age|
+---+
| 30|
| 20|
| 40|
+---+

(4)"age+1"数据

报错
注意:涉及到运算的时候, 每列都必须使用$, 或者采用引号表达式:单引号+字段名
scala> df.select($"age"+1).show
+---------+
|(age + 1)|
+---------+
| 31|
| 21|
| 41|
+---------+
scala> df.select('age +1).show
+---------+
|(age + 1)|
+---------+
| 31|
| 21|
| 41|
+---------+
(5)查看"age"大于"20"的数据
scala> df.filter('age >20).show
+---+--------+
|age|username|
+---+--------+
| 30|zhangsan|
| 40| wangwu|
+---+--------+
(6)按照"age"分组,查看数据条数
scala> df.groupBy("age").count.show
+---+-----+
|age|count|
+---+-----+
| 30| 1|
| 20| 1|
| 40| 1|
+---+-----+
2.4、RDD 转换为 DataFrame
在 IDEA 中开发程序时,如果需要 RDD 与 DF 或者 DS 之间互相操作,那么需要引入import spark.implicits._
这里的 spark 不是 Scala 中的包名,而是创建的 sparkSession 对象的变量名称,所以必须先创建 SparkSession 对象再导入。这里的 spark 对象不能使用 var 声明,因为 Scala 只支持val 修饰的对象的引入。
spark-shell 中无需导入,自动完成此操作
scala> val rdd=sc.makeRDD(List(1,2,3,4))
scala> val df=rdd.toDF("id")
df: org.apache.spark.sql.DataFrame = [id: int]
scala> df.show
+---+
| id|
+---+
| 1|
| 2|
| 3|
| 4|
+---+

2.5、 DataFrame 转换为 RDD
DataFrame 其实就是对 RDD 的封装,所以可以直接获取内部的 RDD
scala> df.rdd
res39: org.apache.spark.rdd.RDD[org.apache.spark.sql.Row]

注意:此时得到的 RDD 存储类型为 Row
3、 DataSet
3.1、创建 DataSet
DataSet 是具有强类型的数据集合,需要提供对应的类型信息
(1)使用样例类序列创建 DataSet
scala> case class Person(name: String, age: Long) ")
defined class Person
scala> val list=List(Person("zhangsan",30),Person("lisi",20))
list: List[Person] = List(Person(zhangsan,30), Person(lisi,20))
scala> val ds = list.toDS
ds: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]
scala> ds.show
+--------+---+
| name|age|
+--------+---+
|zhangsan| 30|
| lisi| 20|
+--------+---+
注意:在实际使用的时候,很少用到把序列转换成DataSet,更多的是通过RDD来得到DataSet
3.2、RDD 转换为 DataSet
SparkSQL 能够自动将包含有 case 类的 RDD 转换成 DataSet,case 类定义了 table 的结构,case类属性通过反射变成了表的列名。Case 类可以包含诸如 Seq 或者 Array 等复杂的结构。
scala> val rdd=sc.makeRDD(List(Emp(30,"zhangsan"),Emp(20,"lisi")))
rdd: org.apache.spark.rdd.RDD[Emp] = ParallelCollectionRDD[95] at makeRDD at <console>:26
scala> val ds=rdd.toDS
ds: org.apache.spark.sql.Dataset[Emp] = [age: bigint, username: string]
scala> ds.show
+---+--------+
|age|username|
+---+--------+
| 30|zhangsan|
| 20| lisi|
+---+--------+
scala> val rdd=sc.makeRDD(List(1,2,3,4))
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[99] at makeRDD at <console>:24
scala> val ds = rdd.toDS
ds: org.apache.spark.sql.Dataset[Int] = [value: int]
scala> ds.show
+-----+
|value|
+-----+
| 1|
| 2|
| 3|
| 4|
+-----+
3.3、DataSet 转换为 RDD
DataSet 其实也是对 RDD 的封装,所以可以直接获取内部的 RDD
scala> val ds = rdd.toDS
ds: org.apache.spark.sql.Dataset[Emp] = [age: bigint, username: string]
scala> ds.show
+---+--------+
|age|username|
+---+--------+
| 30|zhangsan|
| 20| lisi|
+---+--------+
scala> val rdd1=ds.rdd
rdd1: org.apache.spark.rdd.RDD[Emp] = MapPartitionsRDD[109] at rdd at <console>:25
3.4、DataFrame 转换为 DataSet
DataFrame 其实是 DataSet 的特例,所以它们之间是可以互相转换的。
scala> val df = spark.read.json("bin/input/user.json")
df: org.apache.spark.sql.DataFrame = [age: bigint, username: string]
scala> df
res43: org.apache.spark.sql.DataFrame = [age: bigint, username: string]
scala> df.show
+---+--------+
|age|username|
+---+--------+
| 30|zhangsan|
| 20| lisi|
| 40| wangwu|
+---+--------+
scala> case class Emp( age:Long, username:String)
defined class Emp
scala> val ds = df.as[Emp]
ds: org.apache.spark.sql.Dataset[Emp] = [age: bigint, username: string]
scala> ds.show
+---+--------+
|age|username|
+---+--------+
| 30|zhangsan|
| 20| lisi|
| 40| wangwu|
+---+--------+
3.5、DataSet 转换为 DataFrame
scala> ds.toDF
res46: org.apache.spark.sql.DataFrame = [age: bigint, username: string]

4、RDD、DataFrame、DataSet 三者的关系
在 SparkSQL 中 Spark 为我们提供了两个新的抽象,分别是 DataFrame 和 DataSet。他们 和 RDD
有什么区别呢?首先从版本的产生上来看:
➢ Spark1.0 => RDD
➢ Spark1.3 => DataFrame
➢ Spark1.6 => Dataset
如果同样的数据都给到这三个数据结构,他们分别计算之后,都会给出相同的结果。不 同是的他们的执行效率和执行方式。在后期的 Spark 版本中,DataSet 有可能会逐步取代 RDD 和 DataFrame 成为唯一的 API 接口
4.1、三者的共性
➢ RDD、DataFrame、DataSet 全都是 spark 平台下的分布式弹性数据集,为处理超大型数
据提供便利;
➢ 三者都有惰性机制,在进行创建、转换,如 map 方法时,不会立即执行,只有在遇到
Action 如 foreach 时,三者才会开始遍历运算;
➢ 三者有许多共同的函数,如 filter,排序等;
➢ 在对 DataFrame 和 Dataset 进行操作许多操作都需要这个包:import spark.implicits._(在
创建好 SparkSession 对象后尽量直接导入)
➢ 三者都会根据 Spark 的内存情况自动缓存运算,这样即使数据量很大,也不用担心会
内存溢出
➢ 三者都有 partition 的概念
➢ DataFrame 和 DataSet 均可使用模式匹配获取各个字段的值和类型
4.2、三者的区别
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 之后可以很自由的获得每一行的信息
4.3、三者的互相转换

5、IDEA 开发 SparkSQL
5.1、添加依赖
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.12</artifactId>
<version>3.0.0</version>
</dependency>
5.2、代码
package com.atguigu.bigdata.spark.sql
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
/**
* @author shkstart
* @create 2021-12-26 17:17
*/
object Spark01_SparkSQL_Basic {
def main(args: Array[String]): Unit = {
//TODO 创建SparkSQL的运行环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL")
val spark = SparkSession.builder().config(sparkConf).getOrCreate()
import spark.implicits._
//TODO 执行逻辑操作
//TODO DataFrame
//val df = spark.read.json("datas/user.json")
//df.show()
//TODO DataFrame => SQL
// df.createOrReplaceTempView("user")
// spark.sql("select * from user").show
// spark.sql("select age from user").show
// spark.sql("select avg(age) from user").show
//TODO DataFrame => DSL
// df.select("age","username").show
// df.select($"age"+1).show
// df.select('age + 1).show
//TODO DataSet
//DataFrame 其实是特定范型的DataSet
// val seq = Seq(1,2,3,4)
// val ds = seq.toDS()
// ds.show()
//TODO RDD<=>DataFrame
val rdd = spark.sparkContext.makeRDD(List((1,"zhangsan",30),(2,"lisi",40)))
val df: DataFrame = rdd.toDF("id","name","age")
val rowRdd: RDD[Row] = df.rdd
//TODO DataFrame <=> DataSet
val ds: Dataset[User] = df.as[User]
val df1: DataFrame = ds.toDF()
//TODO RDD <=> DataSet
val ds1: Dataset[User] = rdd.map {
case (id, name, age) => {
User(id, name, age)
}
}.toDS()
val userRdd: RDD[User] = ds1.rdd
//TODO 关闭环境
spark.close()
}
case class User(id:Int,name:String,age:Int)
}
6、用户自定义函数
用户可以通过 spark.udf 功能添加自定义函数,实现自定义功能
6.1、UDF
package com.atguigu.bigdata.spark.sql
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
/**
* @author shkstart
* @create 2021-12-26 17:17
*/
object Spark01_SparkSQL_UDF {
def main(args: Array[String]): Unit = {
//TODO 创建SparkSQL的运行环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL")
val spark = SparkSession.builder().config(sparkConf).getOrCreate()
import spark.implicits._
//TODO 执行逻辑操作
val df = spark.read.json("datas/user.json")
df.createOrReplaceTempView("user")
spark.udf.register("prefixName",(name:String)=>{
"Name:" + name
})
spark.sql("select age,prefixName(username) from user").show
//TODO 关闭环境
spark.close()
}
case class User(id:Int,name:String,age:Int)
}
+---+--------------------+
|age|prefixName(username)|
+---+--------------------+
| 30| Name:zhangsan|
| 20| Name:lisi|
| 40| Name:wangwu|
+---+--------------------+
6.2、UDAF
package com.atguigu.bigdata.spark.sql
import org.apache.spark.SparkConf
import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, LongType, StructField, StructType}
object Spark03_SparkSQL_UDAF {
def main(args: Array[String]): Unit = {
// TODO 创建SparkSQL的运行环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL")
val spark = SparkSession.builder().config(sparkConf).getOrCreate()
val df = spark.read.json("datas/user.json")
df.createOrReplaceTempView("user")
spark.udf.register("ageAvg", new MyAvgUDAF())
spark.sql("select ageAvg(age) from user").show
// TODO 关闭环境
spark.close()
}
/*
自定义聚合函数类:计算年龄的平均值
1. 继承UserDefinedAggregateFunction
2. 重写方法(8)
*/
class MyAvgUDAF extends UserDefinedAggregateFunction{
// 输入数据的结构 : Int
override def inputSchema: StructType = {
StructType(
Array(
StructField("age", LongType)
)
)
}
// 缓冲区数据的结构 : Buffer
override def bufferSchema: StructType = {
StructType(
Array(
StructField("total", LongType),
StructField("count", LongType)
)
)
}
// 函数计算结果的数据类型:Out
override def dataType: DataType = LongType
// 函数的稳定性
override def deterministic: Boolean = true
// 缓冲区初始化
override def initialize(buffer: MutableAggregationBuffer): Unit = {
//buffer(0) = 0L
//buffer(1) = 0L
buffer.update(0, 0L)
buffer.update(1, 0L)
}
// 根据输入的值更新缓冲区数据
override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
buffer.update(0, buffer.getLong(0)+input.getLong(0))
buffer.update(1, buffer.getLong(1)+1)
}
// 缓冲区数据合并
override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
buffer1.update(0, buffer1.getLong(0) + buffer2.getLong(0))
buffer1.update(1, buffer1.getLong(1) + buffer2.getLong(1))
}
// 计算平均值
override def evaluate(buffer: Row): Any = {
buffer.getLong(0)/buffer.getLong(1)
}
}
}
package com.atguigu.bigdata.spark.sql
import org.apache.spark.SparkConf
import org.apache.spark.sql.expressions.{Aggregator, MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, LongType, StructField, StructType}
import org.apache.spark.sql.{Encoder, Encoders, Row, SparkSession, functions}
object Spark03_SparkSQL_UDAF1 {
def main(args: Array[String]): Unit = {
// TODO 创建SparkSQL的运行环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL")
val spark = SparkSession.builder().config(sparkConf).getOrCreate()
val df = spark.read.json("datas/user.json")
df.createOrReplaceTempView("user")
spark.udf.register("ageAvg", functions.udaf(new MyAvgUDAF()))
spark.sql("select ageAvg(age) from user").show
// TODO 关闭环境
spark.close()
}
/*
自定义聚合函数类:计算年龄的平均值
1. 继承org.apache.spark.sql.expressions.Aggregator, 定义泛型
IN : 输入的数据类型 Long
BUF : 缓冲区的数据类型 Buff
OUT : 输出的数据类型 Long
2. 重写方法(6)
*/
case class Buff( var total:Long, var count:Long )
class MyAvgUDAF extends Aggregator[Long, Buff, Long]{
// z & zero : 初始值或零值
// 缓冲区的初始化
override def zero: Buff = {
Buff(0L,0L)
}
// 根据输入的数据更新缓冲区的数据
override def reduce(buff: Buff, in: Long): Buff = {
buff.total = buff.total + in
buff.count = buff.count + 1
buff
}
// 合并缓冲区
override def merge(buff1: Buff, buff2: Buff): Buff = {
buff1.total = buff1.total + buff2.total
buff1.count = buff1.count + buff2.count
buff1
}
//计算结果
override def finish(buff: Buff): Long = {
buff.total / buff.count
}
// 缓冲区的编码操作
override def bufferEncoder: Encoder[Buff] = Encoders.product
// 输出的编码操作
override def outputEncoder: Encoder[Long] = Encoders.scalaLong
}
}
package com.atguigu.bigdata.spark.sql
import org.apache.spark.SparkConf
import org.apache.spark.sql.expressions.Aggregator
import org.apache.spark.sql.{Dataset, Encoder, Encoders, SparkSession, TypedColumn, functions}
object Spark03_SparkSQL_UDAF2 {
def main(args: Array[String]): Unit = {
// TODO 创建SparkSQL的运行环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL")
val spark = SparkSession.builder().config(sparkConf).getOrCreate()
import spark.implicits._
val df = spark.read.json("datas/user.json")
// 早期版本中,spark不能在sql中使用强类型UDAF操作
// SQL & DSL
// 早期的UDAF强类型聚合函数使用DSL语法操作
val ds: Dataset[User] = df.as[User]
// 将UDAF函数转换为查询的列对象
val udafCol: TypedColumn[User, Long] = new MyAvgUDAF().toColumn
ds.select(udafCol).show
// TODO 关闭环境
spark.close()
}
/*
自定义聚合函数类:计算年龄的平均值
1. 继承org.apache.spark.sql.expressions.Aggregator, 定义泛型
IN : 输入的数据类型 User
BUF : 缓冲区的数据类型 Buff
OUT : 输出的数据类型 Long
2. 重写方法(6)
*/
case class User(username:String, age:Long)
case class Buff( var total:Long, var count:Long )
class MyAvgUDAF extends Aggregator[User, Buff, Long]{
// z & zero : 初始值或零值
// 缓冲区的初始化
override def zero: Buff = {
Buff(0L,0L)
}
// 根据输入的数据更新缓冲区的数据
override def reduce(buff: Buff, in: User): Buff = {
buff.total = buff.total + in.age
buff.count = buff.count + 1
buff
}
// 合并缓冲区
override def merge(buff1: Buff, buff2: Buff): Buff = {
buff1.total = buff1.total + buff2.total
buff1.count = buff1.count + buff2.count
buff1
}
//计算结果
override def finish(buff: Buff): Long = {
buff.total / buff.count
}
// 缓冲区的编码操作
override def bufferEncoder: Encoder[Buff] = Encoders.product
// 输出的编码操作
override def outputEncoder: Encoder[Long] = Encoders.scalaLong
}
}
Spark SQL 是 Spark 用于结构化数据处理的模块,提供 DataFrame 和 DataSet API,支持 SQL 查询和 Hive 整合。其特点包括易整合、统一的数据访问方式和标准连接。DataFrame 是具有 Schema 的数据容器,而 DataSet 是 DataFrame 的扩展,提供类型安全检查。SparkSession 是 SQL 查询的起点,替代了 SQLContext 和 HiveContext。DataFrame 和 DataSet 之间以及与 RDD 之间可以互相转换,具有相似的操作和转换方法,但效率和类型安全性有所不同。

被折叠的 条评论
为什么被折叠?



