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

分布式计算平台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中获取
      • 累加器:全局分布式计数累加
    • 概念以及调度

      • 所有概念代表的含义
      • 程序运行的过程
  2. 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
  3. 数据结构抽象的定义以及转换

    • 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函数
  2. 电影评分的案例
    • 练习DSL函数编程
  3. Dataset的优势
  4. SparkSQL数据源
    • 读那些数据
    • 保存到那些数据中
  5. 自定义UDF
    • DSL中的UDF
    • SQL中的UDF
  6. 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

    image-20201222102216062

  • DSL

    image-20201222102231027

  • 不论哪种方式,底层都是转换为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.it.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()
      
      
      
        }
      }
      
      
    • 结果

      image-20201222105718037

  • DSL:保存到MySQL中

    • 代码

      package bigdata.it.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.it.cn:3306/?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true",
                "db_test.tb_top10_movies",
                new Properties()
              )
      
      
          /**
            * step3:释放资源
            */
          spark.stop()
      
      
      
        }
      }
      
      
    • 结果

      image-20201222113159666

  • 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、转换

image-20201222115514661

4、对比

  • 性能:DataSet性能是最优的,在编译时,可以基于底层的字段进行优化、发现问题

    image-20201222115104261

六、外部数据源

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.it.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.it.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()
      }
    
    }
    
    

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

  • 基本原理

    image-20201222151547204

  • 启动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.it.cn:9083</value>
        </property>
    </configuration>
    
    • 集群模式,需要分发给Spark所有节点
  • spark-shell

    • 启动spark-shell

      bin/spark-shell --master local[2]
      
    • 方式一:加载表的数据变成DF

      image-20201222153548404

      • 一般常用语DSL开发,变成DF以后,调用DSL函数来做处理
    • 方式二:直接写SQL操作表

      image-20201222153923295

      • 一般用于使用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.it.cn:9083")
            //开启支持Hive
            .enableHiveSupport()
            .getOrCreate()
      
    • 方式一:读取Hive的表变成DF

      image-20201222154522933

    • 方式二:直接运行SQL语句来实现

      image-20201222154618320

七、自定义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语句

    image-20201222160021547

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.it.cn \
    --master local[2]
    

    image-20201222160620886

  • 启动Beeline客户端

    /export/server/spark/bin/beeline
    Beeline version 1.2.1.spark2 by Apache Hive
    beeline> !connect jdbc:hive2://node1.it.cn:10000
    Connecting to jdbc:hive2://node1.it.cn:10000
    Enter username for jdbc:hive2://node1.it.cn:10000: root
    Enter password for jdbc:hive2://node1.it.cn:10000: ****
    

    image-20201222160812087

  • 查看监控

    image-20201222161315722

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.it.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>

</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>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值