1.SparkSQL概述
- (1) 定义:SparkSQL是基于SparkCore专门为模块化计算设计的模块
- (2) 功能:
提供SQL和DSL开发接口,将SQL或者DSL语句转换为SparkCore程序,实现结构化的数据处理 - (3) 特点
- ① Integrated:集成了大多数的开发接口
- DSL:函数式编程实现开发,可使用DSL函数、RDD函数以及SQL关键字形成的函数
- SQL:使用SQL语句编程开发
- ② Uniform Data Access:统一化的数据访问
封装了常用的所有数据源读写接口,文件(csv/tsv/parquet/orc/json)和数据库(MySQL、Hive) - ③ Hive Integration:Hive的集成
兼容Hive的开发方式,可直接访问Hive数仓 - ④ Standard Connectivity:标准的数据连接接口
SQL、JAR、JDBC、beeline等
- ① Integrated:集成了大多数的开发接口
- (4) 应用场景
- ① 离线:代替SparkCore:SQL + DSL,访问Hive数据仓库中的数据进行处理
- ② 实时:StructStreaming(结构化流计算)
2.SparkSQL代码开发模板
import org.apache.spark.sql.SparkSession
/**
* @Todo: SparkSQL代码模板
* @auther: Cjp
* @date: 2021/7/15 20:29
*/
object saprkSqlMode {
def main(args: Array[String]): Unit = {
//todo:1-构建驱动对象:sparksession
val spark:SparkSession = SparkSession
.builder()//构建一个建造器
.master("local[2]")//配置运行模式
.appName(this.getClass.getSimpleName.stripSuffix("$"))//设置程序名称
// .config("key","value")//配置其他属性(内存,核数)
.getOrCreate()//返回一个sparksession对象
//更改日志级别
spark.sparkContext.setLogLevel("WARN")
//todo:2-实现处理的逻辑
//step1:读取数据
//step2:转换数据
//step3:保存结果
//todo:3-释放资源
spark.stop()
}
}
- (1) sparkDSL:函数式编程
- ① 提供了SQL的关键字函数、兼容RDD的转换函数
- ② 与RDD区别:调用函数不一样
- DSL函数 :SQL关键字函数:select、where、groupBy、orderBy、limit、agg
- RDD函数 :结构化数据处理的函数:map、flatMap、filter
- (2) sparkSQL:基于SQL进行处理,开发流程如下:
- ① 将DF或者DS注册为一个只读表:视图
- ② 通过SQL对视图进行处理,返回一个新的DF或者DS
3.SparkCore与SparkSQL是设计对比
-
(1) SparkCore的设计
- ① 设计:
将所有数据放入分布式数据集合,调用集合的转换函数来实现处理,返回新的结果集合 - ② 实现:
- 数据抽象:RDD【分布式集合:数据】
- 数据驱动:SparkContext
- ③ 应用场景
使用函数式编程对各种数据源的数据进行分布式的计算处理 - ④ 瓶颈
RDD中只有数据,没有数据的Schema,SparkCore处理结构化数据不如SQL更方便直观
- ① 设计:
-
(2) SparkSQL的设计
- ① 设计:
将所有数据放入分布式数据表中,使用SQL或者DSL函数来实现处理,返回新的数据表 - ② 实现
- 数据抽象:DataSet / DataFrame 【分布式表:数据 + Schema】
- 数据驱动:SparkSession
- ③ 应用场景
使用SQL或者DSL对结构化数据源的数据进行分布式的计算处理
- ① 设计:
-
(3) 数据抽象之间的区别与联系
-
① 数据抽象设计概述
- RDD:SparkCore中的分布式集合,用于存储数据
- DataFrame(简称DF):早期的SparkSQL中分布式表的设计(1.3版本参考Python的数据结构实现)
- DataSet(简称DS):后期SparkSQL中分布式表的设计
- 1.6版本开始引用(参考Flink设计),保留了DataFrame
- 2.0版本开始,将DF作为DS的一种特殊形式,只保留了DataSet数据结构
-
② 关系与区别
- RDD:RDD【T】:数据 + 支持泛型
- DataFrame:DataFrame:数据 + Schema
- 没有泛型,固定为Row类型
- DataFrame = DataSet[Row]
- DataSet:DataSet【T】 :数据 + Schema + 泛型
-
③ 关系转换
- DF/DS转RDD(使用函数)
- .rdd:从DS或者DF取出RDD
- .schema:从DS或者DF取出Schema
- RDD转DF/DS
- 方式一:反射类型推断
- 方式二:自定义Schema
- DF/DS转RDD(使用函数)
-
-
(4) RDD转DF/DS实现
- ① 反射类型推断
- 功能 :将RDD中的泛型进行修改,将没有Schema类型,更改为有Schema类型,将每条数据从String类型变成一个JavaBean(样例类)
- 使用场景 :适合于Schema比较固定的场景
- ② 自定义Schema
- 功能 :自定义Schema,与RDD合并构建DataFrame
- 使用场景 :适合于字段不固定的场景
- ① 反射类型推断
4.SparkDSL与SparkSQL是设计对比
-
(1) DSL开发
- ① 利用函数式编程的方式来开发SparkSQL程序,类似于SparkCore方式
- ② 流程
- step1:读取数据放入DF或者DS
- step2:调用函数来对DF或者DS进行转换
- RDD的转换函数:map、flatMap、filter……
- SparkSQL中的DSL函数:select、where、groupBy、limit、orderBy
- step3:保存计算以后的结果
-
(2) SQL开发
- ① 利用SQL方式来开发SparkSQL程序,类似于Hive中的使用方式
- ② 流程
- step1:读取数据放入DF或者DS
- step2:将DF或者DS注册为视图,调用SQL语句来实现处理
- step3:保存计算以后的结果
-
(3) 性能
- ① DSL与SQL在性能上没有差异
- ② SparkSQL的底层还是SparkCore,所有计算最终还是由RDD的转换函数来实现
- ③ DS或者DF继承了RDD的特性
- 所有转换都是Lazy模式
- DS或者DF也继承了RDD的五大特性
- 也拥有persist和checkpoint
5.Spark的数据源Source、Sink和SaveMode模式
- (1) SparkSQL有哪些常用的数据源source接口
- ① Parquet文件(SparkSQL默认读取的文本类型就是Parquet)
- ② JSON文件
- ③ CSV文件(以逗号分隔的文件)
- ④ MySQL数据(jdbc文件)
- ⑤ 调用代码格式:spark.read.format(“格式”).load(“路径”)
spark.read.csv/partquet/orc/json/jdbc/table
举例:
spark.read.format("json").load("datas/people.json")
//等同于
spark.read.json("datas/people.json")
- (2) SparkSQL的数据源Sink
- ① 可保存的格式:与数据源接口文件一致
- ② 语法:
df/ds.write.mode
.format
.save/save(Path)/saveAsTable
- save(Path:String):用于保存文本
- save():用于保存数据库
- saveAsTable:保存到Hive表中
//保存方式一:redis、hbase、JDBC
df/ds.foreachPartition(自定义保存的逻辑)
//保存方式二
df/ds.write.mode.jdbc => df/ds.write.mode.format("输出的格式").save("路径")
//保存方式三
df/ds
.write
.mode(SaveMode.Overwrite)
.format("指定保存格式")
.save("保存的路径")/save()/saveAsTable
-
(3) SaveMode:保存模式的几种方式
- ① Append:追加模式
- ② ErrorIfExists:如果数据结果已存在就抛出异常
- ③ OverWrite:覆盖模式
- ④ Ignore:如果数据已存在,就忽略
-
(4) 保存为jdbc格式文件时(即写入数据库,如mysql,oracl)存在的问题:
主键 + 约束的格式导致 如果用追加:主键重复,如果用覆盖,数据丢失
解决方式:- 如果用官方的JDBC:1-建MySQL表时,允许主键自动更新 2-使用追加模式
- 如果不用官方的JDBC:自己写
ds.foreachPartition (part => {
//JDBC
//replace into
})
6.Shuffle分区数
-
(1) 现象:SparkSQL程序不论数据量的多少,在经过聚合shuffle时,RDD分区数会变为200
-
(2) 原因:SparkSQL设计时聚合shuffle的分区数默认为200
-
(3) 弊端:某些情况比如数据量不大时不需要用到200个分区,空间浪费
-
(4) 解决方式:根据数据量调整分区个数
spark.builder
.config("spark.sql.shuffle.partitions","2")
//调整Shuffle分区个数为2
7.SparkSQL的hive集成开发
-
(1) 设计:所有需要访问Hive数据仓库的分布式计算服务,只要知道metastore服务的地址,就可以访问Hive元数据了
-
(2) 开发
- ① DSL
- step1:加载Hive表的数据变成DataSet或者DataFrame:spark.read.table
- step2:使用DSL进行处理
- ② SQL:spark.sql直接使用SQL进行处理
- ① DSL
-
(3) 应用场景
- ① SparkSQL/Impala:分布式内存计算框架:SQL
- ② 代替Hive进行计算:Hive主要作用构建数据仓库
- ③ SQL
- DDL:HiveSQL + MetaStore
- DML:SparkSQL 、Impala
- DQL:SparkSQL 、Impala
8.SparkSQL中自定义UDF
规则:
- (1) SQL:
spark.register.udf(函数名,函数体)
- (2) DSL:
val 函数名 = udf(函数体)
9.SparkSQL的常用开发方式小结
- (1)场景一:代码测试
- ①方式一:IDEA中开发/Spark Shell,直接做本地代码运行测试
- ②方式二:SparkSQL Shell,类似于Hive Shell,直接写SQL
- ③方式三:Beeline,通用JDBC命令行
- (2)场景二:生产运行
- ①方式一:jar包运行,将IDEA中开发的SparkSQL代码打成jar包,通过spark-submit提交运行
- ②方式二:SQL 脚本,将SQL语句封装在SQL脚本中,执行SQL脚本,spark-sql -f xxxx.sql
- ③方式三:JDBC,应用型的SparkSQL的数据处理