前言
Spark SQL是Spark用来处理结构化数据的一个模块,它提供了2个编程抽象:DataFrame和DataSet,并且作为分布式SQL查询引擎的作用。
Spark SQL的特点:
1)易整合;
2)统一的数据访问方式;
3)兼容Hive;
4)标准的数据连接。
DataFrame的介绍:
DataFrame也是一个分布式数据容器。然而DataFrame更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即schema;
Spark SQL记录数据的结构信息,即schema;
同时,与Hive类似,DataFrame也支持嵌套数据类型(struct、array和map)。
DataSet的介绍:
DataFrame只是知道字段(类型),但是不知道字段的类型;
但是DataSet不仅仅知道字段,而且知道字段类型,
样例类被用来在Dataset中定义数据的结构信息,样例类中每个属性的名称直接映射到DataSet中的字段名称;
RDD、DataFrame、DataSet三者之间的转化:
RDD -> Dataset
val ds = rdd.toDS()
RDD -> DataFrame
val df = spark.read.json(rdd)
Dataset -> RDD
val rdd = ds.rdd
Dataset -> DataFrame
val df = ds.toDF()
DataFrame -> RDD
val rdd = df.toJSON.rdd
DataFrame -> Dataset
val ds=df.as[类型]
案例:
项目目录:
利用IDEA工具编写WordCount:
//使用开发工具完成 Spark 的wordcount的开发
//local模式
//创建SparkConf对象
//设置Spark计算框架 的运行环境 local[*]本地
val config: SparkConf = new SparkConf().setMaster("local[*]").setAppName("wordCount")
//创建Spark上下文对象
val sc = new SparkContext(config)
//读取文件,将文件内容一行一行读取出来
val lines: RDD[String] = sc.textFile("input/") //input下的两个文件
//将一行一行数据分解一个一个的单词
val words: RDD[String] = lines.flatMap(_.split(" "))
//为了统计方便 将单词的数据结果进行结构的转换
val wordToOne: RDD[(String, Int)] = words.map((_, 1))
//对转换结构后的数据进行聚合
val wordToSum: RDD[(String, Int)] = wordToOne.reduceByKey(_+_)
//将统计结果采集出来
val result: Array[(String, Int)] = wordToSum.collect()
//将结果打印
//println(result)
result.foreach(println)
程序运行结果:
20/02/22 23:38:20 INFO DAGScheduler: Job 0 finished: collect at SparkWordCount.scala:25, took 0.382258 s
(scala,1)
(word,1)
(hello,2)
20/02/22 23:38:20 INFO SparkContext: Invoking stop() from shutdown hook
用IDEA工具使用SparkSql:
def main(args: Array[String]): Unit = {
//创建配置对象
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSql_02Demo")
//创建SparkSql得环境对象
// val session : SparkSession= new SparkSession(sparkConf) 错误原因 SparkSession构造函数
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
//读取数据DataFrame
val frame:DataFrame= spark.read.text("input/1.txt")
//将DataFtame转换为一张表
frame.createOrReplaceTempView("Test")
//采用sql得语法访问数据
spark.sql("select * from Test").show()
//展示数据
// frame.show()
//释放资源
spark.stop
程序运行结果:
+-----------+
| value|
+-----------+
| hello word|
|hello scala|
+-----------+
使用IDEA工具进行SparkSql类型之间的转换:
object SparkSql_03TransForm { //类型之间转换
def main(args: Array[String]): Unit = {
//创建配置对象
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSql_02Demo")
//创建SparkSql得环境对象
// val session : SparkSession= new SparkSession(sparkConf) 错误原因 SparkSession构造函数
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
//SparkSession 包含了 sparkContext
//进行转换之前 需要引入隐士转换规则
//这里得spark不是包得名字 是SparkSession对象得名字
import spark.implicits._
//创建RDD
val rdd: RDD[(Int, String,Int)] = spark.sparkContext.makeRDD(List( (1, "liuyan", 20), (2, "wen", 24), (3, "huanhuan",20)))
/* //从这一步直接转换DS RDD--> DS 另一种方案 已验证
val UsreRDD: RDD[Usre] = rdd.map {
case (id, name, age) => {
Usre(id, name, age)
}
}
val UsreDS: Dataset[Usre] = UsreRDD.toDS()
val UsreRdd1: RDD[Usre] = UsreDS.rdd
UsreRdd1.foreach(println)*/
//转换为DF RDD--> DataFrame 当前数据没有结构 设置结构
val df: DataFrame = rdd.toDF("id","name","age")
//转换为DS DataFrame--> Dataset 没有结构 需要增加一个样例类
val ds: Dataset[Usre] = df.as[Usre]
//转换为DF Dataset-->DataFrame
val df1: DataFrame = ds.toDF()
//转换为RDD DataFrame-->RDD
val rdd1: RDD[Row] = df1.rdd
//最后类型是Row 特别注意
rdd1.foreach(Row =>{
//获取数据时 可以通过索引访问数据
println(Row.getString(1))
})
//展示数据
// frame.show()
//释放资源
spark.stop
}
}
case class Usre(id:Int,name:String,age:Int)
程序运行结果显示:
20/02/22 23:44:32 INFO CodeGenerator: Code generated in 11.3352 ms
huanhuan
wen
liuyan
20/02/22 23:44:32 INFO Executor: Finished task 1.0 in stage 0.0 (TID 1). 1318 bytes result sent to driver
使用IDEA工具之用户自定义聚合函数练习:
object SparkSql_04UDAF { //用户自定义得聚合函数
def main(args: Array[String]): Unit = {
//创建配置对象
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSql_02Demo")
//创建SparkSql得环境对象
// val session : SparkSession= new SparkSession(sparkConf) 错误原因 SparkSession构造函数
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
//SparkSession 包含了 sparkContext
//进行转换之前 需要引入隐士转换规则
//这里得spark不是包得名字 是SparkSession对象得名字
import spark.implicits._
//用户自定义得聚合函数
//创建聚合函数对象 构造
val udaf=new MyAgeAvgFunction
//注册聚合函数
spark.udf.register("avgAge",udaf)
//使用聚合函数
val frame: DataFrame = spark.read.json("input/user.json")
frame.createOrReplaceTempView("user")
spark.sql("select avgAge(age) from user").show()
//释放资源
spark.stop()
}
}
//声明自定义聚合函数
//1 . 继承UserDefinedAggregateFunction
//2. 实现方法
class MyAgeAvgFunction extends UserDefinedAggregateFunction {
//函数得输入数据结构 inputSchema:在里面增加结构 类型
override def inputSchema: StructType = {
new StructType().add("age",LongType) //指定一个 类型是long
}
//缓冲 计算时数据结构 buffer:缓冲区 增加两个结构 sum count
override def bufferSchema: StructType = {
new StructType().add("sum",LongType).add("count",LongType)
}
//函数返回得数据类型
override def dataType: DataType = DoubleType
//函数是否稳定
override def deterministic: Boolean = true
//计算前缓冲区得初始化 一般是通过顺序访问
override def initialize(buffer: MutableAggregationBuffer): Unit = {
buffer(0)=0L
buffer(1)=0L
}
//根据查询结果更新数据
override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
buffer(0) = buffer.getLong(0)+ input.getLong(0)
buffer(1) = buffer.getLong(1) + 1
}
//将多个节点得缓冲区合并 merge合并
override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
//sum
buffer1(0)=buffer1.getLong(0)+ buffer2.getLong(0)
//count
buffer1(1)=buffer1.getLong(1)+buffer2.getLong(1)
}
// 计算 把最终结果计算
override def evaluate(buffer: Row): Any = {
buffer.getLong(0).toDouble / buffer.getLong(1)
}
}
程序运行结果:
计算年龄的平均值:
{"name":"Andy", "age":30}
{"name":"Justin", "age":30}
+---------------------+
|myageavgfunction(age)|
+---------------------+
| 30.0|
+---------------------+
使用idea工具练习强类型聚合函数:
使用起来更方便
object SparkSql_05UDAFClass {//强类型聚合函数
def main(args: Array[String]): Unit = {
//创建配置对象
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSql_02Demo")
//创建SparkSql得环境对象
// val session : SparkSession= new SparkSession(sparkConf) 错误原因 SparkSession构造函数
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
//SparkSession 包含了 sparkContext
//进行转换之前 需要引入隐士转换规则
//这里得spark不是包得名字 是SparkSession对象得名字
import spark.implicits._
//创建聚合函数
val udaf=new MyAgeAvgClassFunction
//将我们聚合函数转换为查询列
val avgAge: TypedColumn[UserBean, Double] = udaf.toColumn.name("avgAge")
val frame: DataFrame = spark.read.json("input/user.json")
//Dataset对类型做了约束
val userDS: Dataset[UserBean] = frame.as[UserBean]
//应用函数
userDS.select(avgAge).show()
//释放资源
spark.stop
}
}
case class UserBean(name : String,age:BigInt)
case class AvgBuffer(var sum:BigInt,var count:Int)
//声明 用户自定义聚合函数 (强类型)
//1 . 继承 Aggregator 设定泛型
//2. 实现方法
class MyAgeAvgClassFunction extends Aggregator[UserBean,AvgBuffer,Double] {
//初始化
override def zero: AvgBuffer = {
AvgBuffer(0,0)
}
//聚合数据 更新操作
override def reduce(b: AvgBuffer, a: UserBean): AvgBuffer = {
b.sum=b.sum + a.age
b.count=b.count+1
b
}
//缓冲区合并操作
override def merge(b1: AvgBuffer, b2: AvgBuffer): AvgBuffer = {
b1.sum=b1.sum+b2.sum
b1.count=b1.count+b2.count
b1
}
//完成计算
override def finish(reduction: AvgBuffer): Double ={
reduction.sum.toDouble / reduction.count
}
//自定义类型就用这个
override def bufferEncoder: Encoder[AvgBuffer] = Encoders.product
//Double 就用这个
override def outputEncoder: Encoder[Double] = Encoders.scalaDouble
}
程序运行结果:
+------+
|avgAge|
+------+
| 30.0|
+------+
完!