分布式计算平台Spark:SQL(二)
一、回顾
- SparkCore
- 数据源
- Hadoop系列的数据源:Spark是调用了Hadoop的类来实现
- InputFormat:sparkContext.newAPIHadoopRDD(输入类,K,V)
- TableInputFormat
- 封装了:表的对象【定义传递了表名】、Scan对象+Filter【根据查询条件】
- 可以自定义scan对象,传递
- 对表执行了scan操作,读取到所有的RowKey的数据【ResultScanner】
- 将所有Rowkey的数据封装了KV
- K:rowkey:ImmutableBytesWritable
- V:rowkey数据:Result
- OutputFormat:rdd.saveNewApiHadoopFile(输出类)
- TableOutputFormat
- 封装了:表的对象【定义传递了表名】、Put对象
- 对表执行put对象的操作,就可以实现数据的写入HBASE
- 问题:TableOutputFormat的输入时KeyValue,输出是HBASE
- 解决:Value必须为Mutation的子类【Put,Delete】,Key是什么不重要,会被丢弃
- 一般开发经验:将Key设置为rowkey:ImmutableBytesWritable
- 写MySQL:自己写JDBC
- foreachPartition:每个分区构建一个数据库的连接
// val conn :Connection = null
// rsRdd.foreach(tuple => saveMySQL(tuple,conn)):方式不可行,conn对象没有序列化,不能经过跨机器的传输
- 共享变量
- 广播变量:在driver中定义一个变量,将这个变量广播到所有Executor中,直接让Task从自己所在的Executor中获取
- 累加器:全局分布式计数累加
- 概念以及调度
- 所有概念代表的含义
- 程序运行的过程
- SparkSQL
- 功能:实现在Spark中处理结构化数据,提供SQL开发接口
- 接口:SQL【类似于Hive的SQL语句】、DSL【类似于RDD的编程方式】
- 应用:结构化数据处理、分析、转换,搭配数据仓库使用
- 离线结构化数据分析处理
- 实时结构化数据处理分析
- 实现
- Driver接口
- SparkCore:SparkContext:面向RDD【数据】的统一化处理编程
- SparkSQL:SparkSession:面向表【DataSet结构化:数据 + Schema】数据的统一化处理编程
- SparkSession中包含了SparkContext对象
- 数据结构抽象
- SparkCore:RDD
- 里面存储所有数据,数据没有schema
- schema:列的定义,列的名称、列的类型
- 问题:不能使用SQL方式来处理数据
select col1,col2 ……
- SparkSQL:DataFrame,DataSet
- DataFrame,DataSet:存储了数据,并且存储的数据的schema信息
- 当做分布式的表来看:基于分布式的RDD + schema
- 数据结构抽象的定义以及转换
- RDD:存储了数据,支持各种泛型的存储
- 只知道数据的外部结构,不知道内部结构
- DataFrame:存储了数据和Schema,不支持泛型,所有的数据在DF中都是以Row类型存在的
- 知道数据的内部结构,外部结构的类型比较单一
- DataSet:存储了数据和Schema,支持泛型
- 既知道外部结构,也知道内部结构
- 转换
- DF、DS转为RDD
直接调用.rdd
- RDD转为DF、DS
- 方式一:将RDD的数据类型转换为样例类
toDF
toDS
- 方式二:自定义Schema组合构建DataFrame
- RDD[Row] + Schema:StructType【Array【StructField】】
- DF转换为DS
df.as[样例类]
- DS转换为DF
ds.toDF
二、课程目标
- SparkSQL的两种开发方式
- SQL:基本的SQL语法与Hive一致
- DSL:常用的DSL函数
- 电影评分的案例
- 练习DSL函数编程
- Dataset的优势
- SparkSQL数据源
- 读那些数据
- 保存到那些数据中
- 自定义UDF
- DSL中的UDF
- SQL中的UDF
- SparkSQL在工作中的使用方式
- 方式一:在Idea中开发,打成jar包来运行
- 方式二:命令行直接写SQL语句
- 方式三:写SQL文件,通过shell脚本来运行SQL程序
- 方式四:JDBC方式来访问开发
三、DSL与SQL分析
1、DSL
- 流程:类似于RDD的编程,通过调用函数来实现对数据处理和转换
- 区别
- DSL的这些函数都是对列来进行处理
- RDD中的函数一般都是对每个元素来做处理
- 使用:就是使用特定的DSL函数来实现
- DSL函数
- SQL语句的关键词函数
- select(列的名称/SQL函数的调用):读取哪些列
- where(过滤条件):实现数据的过滤
- groupBy(列的名称):按照某一列实现分组
- agg(聚合函数1、聚合函数2……):统一的聚合函数的定义
- orderBy(列的名称):按照某一列排序
- limit(N):限制结果的输出
- withColumnRenamed:重命名函数
- (要修改的列名,修改后的列名)
- withColumn:添加新的一列
- (新的列的列名,列的数据)
- RDD中同名的函数
- map
- flatMap
- reduceByKey
- filter
- persist
- ……
2、SQL
- 流程:就是写Hive中的SQL语句来实现对数据的处理
- step1:先将DS或者DF注册为一个视图
df/ds.createtmporreplaceView(视图的名称)
- step2:使用SQL语句来对视图进行处理,返回一个新的DS
spark.sql(sql语句)
- 语法:基本与Hive的语法是一致的
3、性能
- SQL
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L66VqJZ9-1617980578491)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222102216062.png)] - DSL
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3LuUu3p0-1617980578492)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222102231027.png)] - 不论哪种方式,底层都是转换为SparkCore程序,通过DAG发现,两个程序的执行的过程是一模一样
- 两种方式在底层的性能上是没有差异的,两种方式你可以任意的选择
四、电影评分案例
1、需求
- 基于电影评分数据:ratings.dat 统计平均评分最高的前10部电影【每部电影至少被评分2000次】
- 要求
- 使用DSL和SQL两种方式实现
- 结果存储在文本和MySQL两种数据源中
2、分析
- 数据的格式
UserID::MovieID::Rating::Timestamp
- SQL实现
- step1:读取数据
val inputData = spark.read.textFile()
- step2:转换数据
//etl:过滤一些不合法的数据
val etldata = inputData.filter
//将etl的数据注册为视图
etldata.createView(tmp_view_movie)
//写SQL对视图进行处理
spark.sql(
select
itemId,
avg(Rating) as avg_rate,
count(1) as numb
from tmp_view_movie
group by itemId
having numb > 2000
order by avg_rate desc
limit 10
)
- step3:保存结果:文件中
ds/df.write
- DSL实现
- step1:读取数据
val inputData = spark.read.textFile()
- step2:转换数据
- step3:保存结果:MySQL
3、实现
- SQL:保存到文本文件中
- 代码
package bigdata.itcast.cn.spark.scala.sql.movie
import org.apache.spark.sql.{DataFrame, Dataset, SaveMode, SparkSession}
/**
* @ClassName SparkSQLMovieCaseSQL
* @Description TODO SQL方式实现电影评分的统计
* @Date 2020/12/22 10:36
* @Create By Frank
*/
object SparkSQLMovieCaseSQL {
def main(args: Array[String]): Unit = {
/**
* step1:初始化资源对象:SparkSession
*/
val spark = SparkSession
//构建建造器
.builder()
//配置建造器
.appName(this.getClass.getSimpleName.stripSuffix("$"))
.master("local[2]")
//构建SparkSession
.getOrCreate()
//调整日志级别
spark.sparkContext.setLogLevel("WARN")
//导入当前SparkSession的隐式转换
import spark.implicits._
//导入DSL函数库
// import org.apache.spark.sql.functions._
/**
* step2:处理逻辑
*/
//todo:1-读取数据
val inputData: Dataset[String] = spark.read.textFile("datas/ml-1m/ratings.dat")
// inputData.printSchema()
// inputData.show(3)
//todo:2-转换数据
//etl:将一列划分为4列,过滤非法数据
val etlData: DataFrame = inputData
//过滤非法数据
.filter(line => line != null && line.trim.split("::").length == 4)
//拆分每一列
.map(line => {
val arr = line.trim.split("::")
(arr(0),arr(1),arr(2).toDouble,arr(3).toLong)
})
//转换为DataFrame,赋予列名
.toDF("userId","itemId","rating","timestamp")
// etlData.printSchema()
// etlData.show()
//先构建视图
etlData.createOrReplaceTempView("tmp_view_movie")
//写SQL做分析
val rsData: DataFrame = spark.sql(
"""
|select
| itemId,
| round(avg(rating),2) as avgrate,
| count(itemId) as numb
|from tmp_view_movie
|group by itemId
|having numb > 2000
|order by avgrate desc,numb desc
|limit 10
""".stripMargin)
//todo:3-保存结果
// rsData.printSchema()
// rsData.show()
rsData
//保存结果
.write
//设置保存的方式
.mode(SaveMode.Overwrite)
//保存到CSV文件中
.csv("datas/output/movie")
/**
* step3:释放资源
*/
spark.stop()
}
}
- 结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yUCtvPqd-1617980578493)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222105718037.png)]
- DSL:保存到MySQL中
- 代码
package bigdata.itcast.cn.spark.scala.sql.movie
import java.util.Properties
import org.apache.spark.sql.{DataFrame, Dataset, SaveMode, SparkSession}
/**
* @ClassName SparkSQLMovieCaseSQL
* @Description TODO DSL方式实现电影评分的统计
* @Date 2020/12/22 10:36
* @Create By Frank
*/
object SparkSQLMovieCaseDSL {
def main(args: Array[String]): Unit = {
/**
* step1:初始化资源对象:SparkSession
*/
val spark = SparkSession
//构建建造器
.builder()
//配置建造器
.appName(this.getClass.getSimpleName.stripSuffix("$"))
.master("local[2]")
//构建SparkSession
.getOrCreate()
//调整日志级别
spark.sparkContext.setLogLevel("WARN")
//导入当前SparkSession的隐式转换
import spark.implicits._
//导入DSL函数库
import org.apache.spark.sql.functions._
/**
* step2:处理逻辑
*/
//todo:1-读取数据
val inputData: Dataset[String] = spark.read.textFile("datas/ml-1m/ratings.dat")
// inputData.printSchema()
// inputData.show(3)
//todo:2-转换数据
val rsData = inputData
//过滤非法数据
.filter(line => line != null && line.trim.split("::").length == 4)
//拆分每一列
.map(line => {
val arr = line.trim.split("::")
(arr(0),arr(1),arr(2).toDouble,arr(3).toLong)
})
//转换为DataFrame,赋予列名
.toDF("userId","itemId","rating","timestamp")
//先查询用到的数据
.select($"itemId",$"rating")
//对电影的id做分组
.groupBy($"itemId")
//实现聚合:统计平均分,统计评分次数
.agg(
//统计平均分
round(avg($"rating"),2).as("avgrate"),
//统计评分次数
count($"itemId").as("numb")
)
//将评分次数高于2000的过滤出来
.where($"numb" > 2000)
//按照评分降序,如果评分相同,按照个数降序
.orderBy($"avgrate".desc,$"numb".desc)
//取前10
.limit(10)
//todo:3-保存结果
// rsData.printSchema()
// rsData.show()
//将结果写入MySQL
rsData
.write
.mode(SaveMode.Overwrite)
.option("driver","com.mysql.cj.jdbc.Driver")
.option("user", "root")
.option("password", "123456")
.jdbc(
"jdbc:mysql://node1.itcast.cn:3306/?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true",
"db_test.tb_top10_movies",
new Properties()
)
/**
* step3:释放资源
*/
spark.stop()
}
}
- 结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MVL3ch3n-1617980578495)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222113159666.png)]
- Shuffle分区数
//用于SparkSQL程序在执行shuffle时,使用的分区数,默认值为200,必须根据程序的数据量自己调整这个值
spark.sql.shuffle.partitions=200
- 调整
val spark = SparkSession
//构建建造器
.builder()
//配置建造器
.appName(this.getClass.getSimpleName.stripSuffix("$"))
.master("local[2]")
//调整SparkSQL经过shuffle的分区数为2
.config("spark.sql.shuffle.partitions",2)
//构建SparkSession
.getOrCreate()
- 掌握的知识点
- 熟练的使用DSL函数
- 保存SparkSQL的结果
- 调整SparkSQL的shuffle分区数
五、DataSet
1、设计
- 数据结构
- RDD:没有Schema
- DataFrame:不支持泛型
- 解决保留Schema,支持泛型
- 需求:SparkSQL读取各种数据源放入程序要变成表,才能以SQL或者DSL对字段来实现表的处理
- 目的:将SparkSQL中读取任何数据都以分布式表的形式来实现存储
- 分布式:底层RDD来实现
- 表的形式:封装Schema
- 实现
- RDD:RDD可以统一数据接口,但是RDD不能作为表的结构处理,RDD中没有列的信息
- RDD优点:统一数据接口,分布式数据结构,高容错的数据结构
- RDD缺点:没有Schema
- DataFrame:基于RDD添加了Schema,延续了RDD优点
- DF缺点:设计中所有数据在RDD中存储时,除了添加了Schema以外,RDD数据类型都会转换为Row
- DataSet:基于RDD,延续了RDD分布式、高可靠等特点,通过RDD中的数据泛型的定义,添加了不同Schema
- 单独定义了Schema类型:StructType
2、优点
- 更加安全
- 性能更好
- 编程会更加的灵活
3、转换
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Xd5yh7i-1617980578496)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222115514661.png)]
注意:虽然rdd->dataFrame与dataFrame->Dataset都是用的样例类但是为了实现强制类型转换
4、对比
- 性能:DataSet性能是最优的,在编译时,可以基于底层的字段进行优化、发现问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P94oocJS-1617980578497)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222115104261.png)]
六、外部数据源
1、加载数据load
- 读取数据的实现:非常类似于MapReduce中读数据的实现【InputFormat】
- 方式
spark.read => DataFrameReader
- 语法
规范的语法:spark.read.format( 读取数据类型 ).load( 数据所在的位置 )
//SparkSQL将常用的数据读取的接口做了封装
spark.read.textFile = spark.read.format("text").load(path)
spark.read.json = spark.read.format("json").load(path)
- 常见的读取数据源
package bigdata.itcast.cn.spark.scala.sql.dataSource
import java.util.Properties
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}
/**
* @ClassName SparkSQLMode
* @Description TODO SparkSQL常见的读取数据源
* 文件:textFile/json/parquet/csv/tsv
* 数据库:MySQL、Hive
* @Date 2020/12/14 13:55
* @Create By Frank
*/
object SparkSQLDataSource {
def main(args: Array[String]): Unit = {
/**
* step1:初始化资源
*/
val spark = SparkSession.builder()
.appName(this.getClass.getSimpleName.stripSuffix("$"))
.master("local[2]")
.getOrCreate()
//设置日志级别
spark.sparkContext.setLogLevel("WARN")
//导入隐式转换
import spark.implicits._
import org.apache.spark.sql.functions._
/**
* step2:处理计算
*/
//todo:1-读取数据
//读取普通文件
// val textData = spark.read.textFile("datas/resources/people.txt")
//读取parquet文件:Spark中默认的文件类型
val parquetData1 = spark.read.parquet("datas/resources/users.parquet")
val parquetData2 = spark.read.format("parquet").load("datas/resources/users.parquet")
val parquetData3 = spark.read.load("datas/resources/users.parquet")
// parquetData1.show()
// parquetData2.show()
// parquetData3.show()
//读取JSON类型文件
val jsonData1: DataFrame = spark.read.json("datas/resources/people.json")
// jsonData1.show()
val jsonData2: Dataset[String] = spark.read.textFile("datas/resources/people.json")
jsonData2
.select(
get_json_object($"value","$.name"),
get_json_object($"value","$.age")
)
// .show()
//读取任意固定分隔符的文件:默认读取CSV文件,可以指定分隔符
val tsvData = spark
.read
//指定读取文件的分割符为制表符
.option("sep","\t")
//用文件的第一行作为列的名称
.option("header","true")
//自动推断数据类型
.option("inferSchema","true")
.csv("datas/ml-100k/u.dat")
// tsvData.printSchema()
// tsvData.show()
/**
* todo:读取MySQL的数据
* def jdbc(url: String, table: String, properties: Properties)
*
*
* def jdbc(
* url: String,:连接的MYSQL的URL
* table: String,:读取的表的名称
* columnName: String,:按照哪一列来划分分区
* lowerBound: Long,:这一列值的下限
* upperBound: Long,:这一列值的上限
* numPartitions: Int,:自定义分区的个数
* connectionProperties: Properties): 定义属性的配置
*
*
* def jdbc(
* url: String,
* table: String,
* predicates: Array[String],
* connectionProperties: Properties)
*
*
* val jdbcDF = spark.read
* .format("jdbc")
* .option("url", "jdbc:postgresql:dbserver")
* .option("dbtable", "schema.tablename")
* .option("user", "username")
* .option("password", "password")
* .load()
*/
val url: String = "jdbc:mysql://node1.itcast.cn:3306/?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true"
val table: String = "db_test.tb_top10_movies"
// 存储用户和密码等属性
val props: Properties = new Properties()
props.put("driver", "com.mysql.cj.jdbc.Driver")
props.put("user", "root")
props.put("password", "123456")
spark.read
.jdbc(url,table,props)
.show()
//todo:2-处理数据
//todo:3-输出数据
/**
* step3:释放资源
*/
Thread.sleep(1000000L)
spark.stop()
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IoMYYW03-1617980578497)(C:/Users/MSI-NB/AppData/Roaming/Typora/typora-user-images/image-20201226184538304.png)]
2、保存数据save
- 保存数据的实现:非常类似于MapReduce中写数据的实现【OutputFormat】
- 方式
ds.write => DataFrameWriter
- 语法
规范的语法:ds.write.mode.format( 保存数据类型 ).save()
//SparkSQL将常用数据输出接口封装了
ds.write.mode.cvs(path) => ds.write.mode.format("csv").save
ds.write.mode.jdbc =>
ds.write
.format("jdbc")
.option("url", "jdbc:postgresql:dbserver")
.option("dbtable", "schema.tablename")
.option("user", "username")
.option("password", "password")
.save()
- 常见的输出数据源
- 文件
- MySQL
3、集成Hive
- 基本原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vez3FrJn-1617980578498)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222151547204.png)] - 启动Hive的MetaStore服务
start-dfs.sh
cd /export/server/hive
bin/hive-daemon.sh metastore
- spark中创建hive-site.xml,执行metastore服务地址
cd /export/server/spark
vim conf/hive-site.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>hive.metastore.uris</name>
<value>thrift://node1.itcast.cn:9083</value>
</property>
</configuration>
- 集群模式,需要分发给Spark所有节点
- spark-shell
- 启动spark-shell
bin/spark-shell --master local[2]
- 方式一:加载表的数据变成DF
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hP8iSfmF-1617980578498)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222153548404.png)]
- 一般常用语DSL开发,变成DF以后,调用DSL函数来做处理
- 方式二:直接写SQL操作表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QJK5FjQE-1617980578499)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222153923295.png)]
- 一般用于使用SQL进行开发,直接对Hive的表运行SQL处理
- IDEA中集成
- 注意:IDEA中集成Hive开发SparkSQL,必须申明Metastore的地址和启用Hive的支持
val spark = SparkSession.builder()
.appName(this.getClass.getSimpleName.stripSuffix("$"))
.master("local[2]")
//设置属性的值:配置Hivemetastore的地址
.config("hive.metastore.uris","thrift://node1.itcast.cn:9083")
//开启支持Hive
.enableHiveSupport()
.getOrCreate()
- 方式一:读取Hive的表变成DF
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jHq3fCXS-1617980578499)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222154522933.png)] - 方式二:直接运行SQL语句来实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2jQIbGrS-1617980578500)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222154618320.png)]
七、自定义UDF
1、问题与设计
- 问题:当SQL/DSL中的函数不能满足业务需求时,可以自定义函数来实现
- 设计:在代码中直接注册一个函数,放在SQL或者DSL中使用即可
2、SQL中使用
- 语法
spark.udf.register(
自定义函数的名称:String,
函数体:func
)
- 测试
/**
* 方式一:在SQL中使用的UDF函数
*/
spark.udf.register(
//函数名
"toLow",
//函数的参数是一个字符串,返回一个小写的字符串
(name:String) => name.toLowerCase
)
spark.sql("select ename,toLow(ename) as lowName from db_hive.emp").show()
3、DSL中使用
- 语法
val 函数的名称 = udf(函数体)
- 测试
/**
* 方式二:在DSL中使用的UDF函数
*/
val toLow = udf(
//直接定义函数体
(name:String) => name.toLowerCase
)
hiveData
.select($"ename",toLow($"ename").as("lowName"))
.show()
八、SparkSQL开发方式
1、代码开发
- 工作中比较常用的方式
- 形式:在IDEA中进行DSL或者SQL代码开发,类似于SparkCore的编程模式
- 运行:打成jar包提交到Spark集群运行
- 应用:ETL、数据分析
2、交互式命令行
- 功能:与Hive的shell命令行类似,主要提供SQL的命令行,用于直接写代码做测试开发
- 启动
cd /export/server/spark
bin/spark-sql --master local[2]
- 执行SQL语句
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jOMhccUR-1617980578500)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222160021547.png)]
3、执行SQL脚本文件
- 类似于Hive的使用
- 在Linux命令行运行SparkSQL的语句
bin/spark-sql -e ”sql语句“
- 在Linux中运行SparkSQL的SQL文件
bin/spark-sql -f SQL文件的地址
- 用于将SQL语句封装到脚本中调度执行
4、ThriftServer
- 应用:需要随机的访问和分析数据仓库中数据,需要一个交互式的命令行
- 测试开发
- 提供给运营、数据分析师
- ThriftServer:类似于Hive中的HiveServer2,这是SparkSQL的服务端
- 启动ThriftServer服务端
SPARK_HOME=/export/server/spark
$SPARK_HOME/sbin/start-thriftserver.sh \
--hiveconf hive.server2.thrift.port=10000 \
--hiveconf hive.server2.thrift.bind.host=node1.itcast.cn \
--master local[2]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uSlvIb2I-1617980578501)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222160620886.png)]
- 启动Beeline客户端
/export/server/spark/bin/beeline
Beeline version 1.2.1.spark2 by Apache Hive
beeline> !connect jdbc:hive2://node1.itcast.cn:10000
Connecting to jdbc:hive2://node1.itcast.cn:10000
Enter username for jdbc:hive2://node1.itcast.cn:10000: root
Enter password for jdbc:hive2://node1.itcast.cn:10000: ****
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QiicdWxu-1617980578501)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222160812087.png)]
- 查看监控
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fh4FO5Gd-1617980578502)(Day42_分布式计算平台Spark:SQL(二).assets/image-20201222161315722.png)]
5、JDBC
import java.sql.{Connection, DriverManager, PreparedStatement, ResultSet}
/**
* SparkSQL 启动ThriftServer服务,通过JDBC方式访问数据分析查询
* i). 通过Java JDBC的方式,来访问Thrift JDBC/ODBC server,调用Spark SQL,并直接查询Hive中的数据
* ii). 通过Java JDBC的方式,必须通过HTTP传输协议发送thrift RPC消息,Thrift JDBC/ODBC server必须通过上面命令启动HTTP模式
*/
object SparkThriftJDBC {
def main(args: Array[String]): Unit = {
// 定义相关实例对象,未进行初始化
var conn: Connection = null
var pstmt: PreparedStatement = null
var rs: ResultSet = null
try {
// TODO: a. 加载驱动类
Class.forName("org.apache.hive.jdbc.HiveDriver")
// TODO: b. 获取连接Connection
conn = DriverManager.getConnection(
"jdbc:hive2://node1.itcast.cn:10000/db_hive",
"root",
"123456"
)
// TODO: c. 构建查询语句
val sqlStr: String =
"""
|select e.empno,e.ename, e.sal, d.dname from emp e join dept d on e.deptno = d.deptno
""".stripMargin
pstmt = conn.prepareStatement(sqlStr)
// TODO: d. 执行查询,获取结果
rs = pstmt.executeQuery()
// 打印查询结果
while (rs.next()) {
println(s"empno = ${rs.getInt(1)}, ename = ${rs.getString(2)}, sal = ${rs.getDouble(3)}, dname = ${rs.getString(4)}")
}
} catch {
case e: Exception => e.printStackTrace()
} finally {
if (null != rs) rs.close()
if (null != pstmt) pstmt.close()
if (null != conn) conn.close()
}
}
}
附录一:SparkSQL Maven依赖
<!-- 指定仓库位置,依次为aliyun、cloudera和jboss仓库 -->
<repositories>
<repository>
<id>aliyun</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
<repository>
<id>jboss</id>
<url>http://repository.jboss.com/nexus/content/groups/public</url>
</repository>
</repositories>
<properties>
<scala.version>2.11.12</scala.version>
<scala.binary.version>2.11</scala.binary.version>
<spark.version>2.4.5</spark.version>
<hadoop.version>2.6.0-cdh5.16.2</hadoop.version>
<hbase.version>1.2.0-cdh5.16.2</hbase.version>
<mysql.version>8.0.19</mysql.version>
</properties>
<dependencies>
<!-- 依赖Scala语言 -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<!-- Spark Core 依赖 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- Spark SQL 依赖 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- Spark SQL 与 Hive 集成 依赖 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive-thriftserver_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql-kafka-0-10_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-avro_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-mllib_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- Hadoop Client 依赖 -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!-- HBase Client 依赖 -->
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>${hbase.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-hadoop2-compat</artifactId>
<version>${hbase.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>${hbase.version}</version>
</dependency>
<!-- MySQL Client 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
<build>
<outputDirectory>target/classes</outputDirectory>
<testOutputDirectory>target/test-classes</testOutputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
<!-- Maven 编译的插件 -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
附录二:Spark离线案例Maven依赖
<repositories>
<repository>
<id>aliyun</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
<repository>
<id>jboss</id>
<url>http://repository.jboss.com/nexus/content/groups/public</url>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<scala.version>2.11.12</scala.version>
<scala.binary.version>2.11</scala.binary.version>
<spark.version>2.4.5</spark.version>
<hadoop.version>2.6.0-cdh5.16.2</hadoop.version>
<mysql.version>8.0.19</mysql.version>
</properties>
<dependencies>
<!-- 依赖Scala语言 -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<!-- Spark Core 依赖 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- Spark SQL 依赖 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- Spark SQL 与 Hive 集成 依赖 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- Hadoop Client 依赖 -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!-- MySQL Client 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- 根据ip转换为省市区 -->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>1.7.2</version>
</dependency>
<!-- 管理配置文件 -->
<dependency>
<groupId>com.typesafe</groupId>
<artifactId>config</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.14.2</version>
</dependency>
</dependencies>
<build>
<outputDirectory>target/classes</outputDirectory>
<testOutputDirectory>target/test-classes</testOutputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
<!-- Maven 编译的插件 -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>