1、SparkSql概述
1.1混乱的前世今生
先出现MapReduce,后本着sql on mr的思路,产生了Hive。
MapReduce执行效率太慢,效率升级后产生了Tez,再效率升级后产生了Spark。
Spark为了方便操作,本着sql on spark的思路产生Shark,但Shark太多的借鉴和依赖Hive,制约了Spark的One Stack Rule Them All既定方针,制约了Spark各个组件的相互集成
最后被Spark团队推翻和停更,推出了sql on spark新项目SparkSql,使sql on spark的发展方向上,得到质的提升。
1.2 SparkSql简介
SparkSql是什么
oSpark SQL是Spark处理数据的一个模块
o专门用来处理结构化数据的模块,像json,parquet,avro,csv,普通表格数据等均可。
o与基础RDD的API不同,Spark SQL中提供的接口将提供给更多关于结构化数据和计算的信息,并针对这些信息,进行额外的处理优化。
SparkSql操作方式说明
oSparkSql shell
类似于hive shell
oDataFrames API
最早专为sql on spark设计的数据抽象,与RDD相似,增加了数据结构scheme描述信息部分。
写spark代码,面向DF(DataFrams缩写)编程,可以与其它Spark应用代码无缝集成。
比RDD更丰富的算子,更有利于提升执行效率、减少数据读取、执行计划优化。
oDataSets API
集成了RDD强类型和DataFrames结构化的优点,官方正强力打造的新数据抽象类型。
写spark代码,面向DS编程,可以与其它Spark应用代码无缝集成。
比RDD更丰富的算子,更有利于提升执行效率、减少数据读取、执行计划优化。
o面向程序接口对接的操作:通过JDBC、ODBC等方式操作SparkSql
通过jdbc、odbc链接后,发送相关的sparksql请求,实现基于sparksql功能开发。
SparkSql的特点
o可以利用SQL、DataFrams API、DataSets API或其它语言调用的基于sparksql模块计算,均是sparkcore执行引擎,其对计算的表达是独立的,即开发人员可以轻松在不同API之间切换实现相同的功能。
o也可以通过命令行、JDBC、ODBC的方式来操作SparkSQL,方便其它数据平台、BI平台使用SparkSql模块。
o在spark应用程序开发中,可以无缝使用SparkSql操作数据。
o可以直接使用Hive表格数据。
o与Hive的兼容性极好:它复用了Hive的前端(去掉驱动mapreduce执行任务的部分)和元数据,因此可以拿过来hivesql的东西在sparksql上运行即可。
并不是100%完全兼容,但绝大多数情况下,不需要改动,或只需要极小的改动!!!
比如个别版本不支持直接insert into table xxx values(xxx…)的插入数据的方式
oSparkSql的应用中,sql是一个重要方面,但不局限制sql。
SparkSql愿景
o写更少的代码
o读更少的数据
o把优化的工作交由底层的优化器运行
把优化工作拿掉,我们并不需要做一些优化工作,也就是小白和高手写出来的应用程序最后的执行效率都是一样的。
1.3 Hadoop生态圈SQL相关重要名词解释
SQL
o面向数据编程的最高级抽象
HQL=Hive Sql
osql on hadoop的落地
Hive on mr
osql on mapreduce引擎
Hive on tez
osql on tez引擎
Shark
o早期发展的sql on spark项目,已废弃
SparkSql
ospark团队主推的sql on spark项目,目前已相对成熟
osql on spark商用的首选
Hive on spark
oHive团队的主推,但没有得到spark团队的强力支持。
o是sql on spark的第2选择,但相对bug和使用复杂度略高
2、SparkSql Shell操作SparkSql
环境进入方式
//直接输入spark-sql+自己想要添加的参数即可,与spark-shell相似
spark-sql [options]
//如指定运行模式
spark-sql local[*]
//如指定运行spark webui服务的端口,解决多人共用一个入口机时候的进入时候报port bind exception的问题
spark-sql --conf spark.ui.port=4075
//也可以用于似于hive -e的方式,直接直接一段sparksql代码
spark-sql –e “sparksql code”
成功进入后状态
操作方式
o使用方式融合几乎全部hive操作:集成了hive前端使用+元数据信息
DDL、DML、DQL
创建库
创建表
删除表
数据装载(不支持直接insert into table … values(…)方式)
更多的系统函数,包含全部的hive系统函数
添加自定义udf/udaf/udtf等
动态自定义分区
hive数据分析数
及其它hive的各项功能
3、DataFrames API操作SparkSql
3.1 开发环境搭建步骤
与之Sbt构建SparkWordCount步骤完全一样。
将Spark-Sql库依赖,加入build.sbt
osbt reload
osbt eclipse
oeclipse项目中进行刷新操作
spark-sql依赖库
“org.apache.spark” %% “spark-sql” % “1.6.2” % “provided”
3.2 DataFrames相关操作
创建DataFrames
package com.tl.job003.sql
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.sql.SQLContext
object SparkSqlTest {
def main(args: Array[String]) {
val conf = new SparkConf()
conf.setMaster(“local”);
conf.setAppName(“TestSparkSql”);
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
// 添加将RDD转化为DataFrame的功能包引入
import sqlContext.implicits._
val df = sqlContext.read.json(“file:\E:\test\job003\sparksql\input_weibo.json”)
df.show();
sc.stop();
}
}
DataFrame常用操作
o常用命令
show
printSchema
select
filter
groupBy
count
orderBy
其它操作类比于sql即可
o综合示例
package com.tl.job003.sql
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.sql.SQLContext
object SparkSqlTest {
def main(args: Array[String]) {
val conf = new SparkConf()
conf.setMaster(“local”);
conf.setAppName(“TestSparkSql”);
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
// 添加将RDD转化为DataFrame的功能包引入
import sqlContext.implicits._
val df = sqlContext.read.json(“file:\E:\test\job003\sparksql\input_weibo.json”)
//默认显示内容的top20
df.show()
// 打印内容对应的表结构
df.printSchema()
// 选择内容当中的某一个列对应的内容
df.select(“content”).show()
// 选择任意列并进行自定义操作
df.select(df(“content”), df(“commentCount”), df(“commentCount”) + 10000).show()
// 选择评论数大于100的数据显示出来
df.filter(df(“commentCount”) > 100).show()
// 按userId进行分组计数统计
df.groupBy(“userId”).count().show()
//分组统计结果按默认升序排列
df.groupBy(“userId”).count().orderBy(“count”).show()
//分组统计结果按降序排列
import org.apache.spark.sql.functions._
df.groupBy(“userId”).count().orderBy(desc(“count”)).show()
sc.stop();
}
}
RDD与DataFrame互操作
o将RDD转化成DataFrame(将无结构化数据转化成有结构化数据)
将一个RDD转化为带Scheme的DataFrame
实现转化的方式有两种
反射推断
程序编码实现数据与结构的对应,达到转化目标。(重点)
package com.tl.job003.sql
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.sql.SQLContext
import org.apache.spark.sql.Row
import org.apache.spark.sql.types.StructType
import org.apache.spark.sql.types.StructField
import org.apache.spark.sql.types.StringType
object SparkSqlTest {
def main(args: Array[String]) {
val conf = new SparkConf()
conf.setMaster(“local”);
conf.setAppName(“TestSparkSql”);
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
// 添加将RDD转化为DataFrame的功能包引入
import sqlContext.implicits._
//将一个RDD转化成对应的DF
//首先定义scheme
val schema =
StructType(
“stdNo name classNo className”.split(" “).map(fieldName => StructField(fieldName, StringType, true)))
// 创建一个Row RDD
val studentRDD = sc.textFile(“file:\E:\test\job003\sparksql\input_rdb_student.txt”)
val rowRDD = studentRDD.map(_.split(”\t")).map(p => Row(p(0), p(1), p(2), p(3)))
// 将schema和RDD进行整合,形成df
val studentDataFrame = sqlContext.createDataFrame(rowRDD, schema)
// df输出测试
studentDataFrame.printSchema()
studentDataFrame.show()
sc.stop();
}
}
o将DataFrame转化成RDD
//提供直接转化成RDD的方式,即df.rdd即可
studentDataFrame.rdd
oSparkSql临时表生成(内存中存放的表,应用结束即消失)
直接通过df注册生成
//将df对象直接注册成一张临时表
df.registerTempTable(“weibo_doc”);
o将RDD转换成DF后,再如上例注册成临时表
o临时表与DataFrame互操作
应用开发中,使用SqlContext调用sql语句生成DataFrame
综合示例
package com.tl.job003.sql
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.sql.SQLContext
object SparkSqlTest {
def main(args: Array[String]) {
val conf = new SparkConf()
conf.setMaster(“local”);
conf.setAppName(“TestSparkSql”);
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
// 添加将RDD转化为DataFrame的功能包引入
import sqlContext.implicits._
val df = sqlContext.read.json(“file:\E:\test\job003\sparksql\input_weibo.json”)
//注册为名称为weibo_doc的临时表
df.registerTempTable(“weibo_doc”);
//执行该sql,并做相应的dataframe操作即可
sqlContext.sql(“select userId,content from weibo_doc order by commentCount desc”).show();
sc.stop();
}
}
DataFrame数据持久化
oparquet数据格式,默认的输入和输出均为该格式
ospark自带:天然集成,强力推荐的数据格式
parquet产生背景
o面向分析型业务的列式存储格式
o由Twitter和Cloudera合作开发,2015年5月从Apache的孵化器里毕业成为Apache顶级项目.
oTwitter的日志结构是复杂的嵌套数据类型,需要设计一种列式存储格式,既能支持关系型数据(简单数据类型),又能支持复杂的嵌套类型的数据,同时能够适配多种数据处理框架。
parquet的优点
o压缩数据,内部自带gzip压缩
o不失真
o减少IO吞吐量
o高效的查询
o多数据处理平台,均支持parquet,包括hive等。
综合示例实现
package com.tl.job003.sql
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.sql.SQLContext
object SparkSqlTest {
def main(args: Array[String]) {
val conf = new SparkConf()
conf.setMaster(“local”);
conf.setAppName(“TestSparkSql”);
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
// 添加将RDD转化为DataFrame的功能包引入
import sqlContext.implicits._
val df = sqlContext.read.json(“file:\E:\test\job003\sparksql\input_weibo.json”)
//注册为名称为weibo_doc的临时表
df.registerTempTable(“weibo_doc”)
//执行该sql,并做相应的dataframe操作即可
val resultDF = sqlContext.sql(“select userId,content from weibo_doc order by commentCount desc”)
resultDF.repartition(1).write.format(“parquet”).save(“file:\E:\test\job003\sparksql\output”);
sc.stop();
}
}
4、DataSets API操作SparkSql
1开发环境搭建步骤
o与DataFrames完全相同
2 DataSets相关操作
o操作说明
DataSet集成了RDD和DataFrame的优点,也称为强类型的DataFrame。
DataSets和DataFrames具有完全相同的成员函数。
两者中,每个行的数据类型不同。DataFrame也可以叫Dataset[Row],即DataFrame是Dataset的一种特定形式。而DataSet的每一行是不固定的,需要模式匹配来确定。
o版本说明
在1.6.2版本DataSet为alpha版测试功能,API方面均没有得到丰富和完善。
在2.0.0开始DataSet得到正式推广使用,由于其API和DataFrame在成员函数中完全对等,在使用上差异极小,由于是强类型,故仅在数据集case class模式匹配时,有明显差别。
5、多数据集抽象类型对比分析
spark抽象数据集列表
oRDD
oDataFrame
oDataSet
相同点
o全都是spark平台下的分布式弹性数据集,为处理超大型数据提供便利
o三者都有惰性机制,在进行Transform操作时不会立即执行,在遇到Action操作时会正式提交作业执行。
o均采用spark的内存运算和优化策略,内存使用和执行效率上均可以得到保障。
o均有partition的概念,便于分布式并行计算处理,达到分而治之。
o均有许多共同的函数,如map、filter、sort等。
o在进行三者的相关操作时候,个别特殊操作时必须引入一个相同的包依赖。( 早期称为 import sqlContext.implicits.,最新版本称为import spark.implicits.)
oDF和DS均可以通过模式匹配获取内部的变量类型和值。
oDF和DS产生于SparkSql,天然支持SparkSql。
区别点
oRDD
不支持SparkSql操作,均需进行转成DF或是DS才行。
类型是安全的,编译时候即可检查出类型错误。(强类型)
机器间通信、IO操作均需要序列化、反序列化对象,性能开销大。
oDataFrame
有scheme的RDD:比RDD增加了数据的描述信息。
比RDD的API更丰富,增加了针对结构化数据API。
只有一个固定类型的DataSet,即为DataFrame=DataSet[Row]
序列化和反序列化时做了结构化优化,减少了不必要的结构化信息的序列化,提高了执行效率。
oDataSet
强类型的DataFrame,与DF有完全相同的成员函数。
每行的类型不固定,需要使用模式匹配case class后,获取实际的类信息、字段类型、字段值。
访问对象数据时,比DF更加直接简单。
在序列化和反序列化时,引入了Encoder机制,达到按需序列化和反序列化,不必像之前整个对象操作了,进一步提高了效率。
应用场景
o使用RDD场景
数据为非结构化,如流媒体等数据
对数据集进行底层的转换、处理、控制
不需要列式处理,而是通过常规的对象.属性来使用数据。
对DF、DS带来的开发效率、执行效率提升不敏感时
o使用DF(必须)
R或是python语言开发者,使用DF
o使用DS(必须)
在编译时就有高度的类型安全,想要有类型的JVM对象,用上Catalyst优化,并得益于Tungsten生成的高效代码
o使用DF、DS场景
需要丰富的语义、高级抽象和特定领域专用的API时
处理需要对半结构化数据进行高级处理,如filter、map、aggregation、average、sum、SQL查询、列式访问或使用lambda函数
在不同的Spark库之间使用一致和简化的API