分布式计算平台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
- 封装了:表的对象【定义传递了表名】、Scan对象+Filter【根据查询条件】
- TableInputFormat
- OutputFormat:rdd.saveNewApiHadoopFile(输出类)
- TableOutputFormat
- 封装了:表的对象【定义传递了表名】、Put对象
- 对表执行put对象的操作,就可以实现数据的写入HBASE
- 问题:TableOutputFormat的输入时KeyValue,输出是HBASE
- 解决:Value必须为Mutation的子类【Put,Delete】,Key是什么不重要,会被丢弃
- 一般开发经验:将Key设置为rowkey:ImmutableBytesWritable
- TableOutputFormat
- InputFormat:sparkContext.newAPIHadoopRDD(输入类,K,V)
-
写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
- DataFrame,DataSet:存储了数据,并且存储的数据的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
- ……
- SQL语句的关键词函数
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
- RDD:RDD可以统一数据接口,但是RDD不能作为表的结构处理,RDD中没有列的信息
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>