分布式计算平台Spark:SQL(二)

一、回顾

  1. 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中获取
  • 累加器:全局分布式计数累加
  • 概念以及调度
  • 所有概念代表的含义
  • 程序运行的过程
  1. 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
  1. 数据结构抽象的定义以及转换
  • 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

二、课程目标

  1. SparkSQL的两种开发方式
  • SQL:基本的SQL语法与Hive一致
  • DSL:常用的DSL函数
  1. 电影评分的案例
  • 练习DSL函数编程
  1. Dataset的优势
  2. SparkSQL数据源
  • 读那些数据
  • 保存到那些数据中
  1. 自定义UDF
  • DSL中的UDF
  • SQL中的UDF
  1. 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>