Spark SQL

1、SparkSession的构建:

       SparkSession:是Spark2.0引入的新概念,为用户提供统一的入口,可以让用户使用Spark的各项功能。

    //构建Spark-sql的环境,使用SparkSession统一入口。SparkSession同时也兼容Spark Core、SQL、Streaming、MLLib,
    // 都可以对这些组成进行环境初始化
    //在之后的构建环境可以使用SparkSession

    //构建SparkSession,此处的命名方式也是固定的,通常使用spark
    val spark: SparkSession = SparkSession
      .builder()
      .appName("Demo01SaprkSessio")
      .master("local")
      //可以通过config做一些配置
      //可以设定并行度
      //      .config("spark.default.parallelism","3")
      .getOrCreate()
2、Spark SQL:

                指的是一个低延迟,交互式的分布式查询引擎。是基于DataFrame的,可使用标准的SQL和HQL来查询数据

        优点:1、速度比MapReduce快大约10倍左右

                   2、自带优化器,能够基于SQL生成代码并自动优化

                   3、同样也是支持分布式查询,

                   4、支持多种语言:scala、python、java

                   5、支持各种常见的数据源,比如scv、josn、orc、jdbc...

        特点:

                   1、集成:SQL与Spark无缝连接,支持多种语言,可以将RDD转化成DF,使复杂的SQL查询变得简单化

                   2、统一的数据访问:提供了统一的接口DF,可以加载和查询不同的数据源的数据

                   3、兼容Hive: 可以实现和hive的连接

                   4、标准连接:具有行业标准的JDBC和ODBC服务器的连接模式

        Spark SQL导入依赖:

    <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_2.12</artifactId>
            <version>3.1.3</version>
        </dependency>
    </dependencies>
3、DataFrame(DF):

                指的是一种以RDD为基础的分布式数据集。类似于传统数据库中的一张表。与RDD最主要的区别就是DF带有Schema元信息。总的来说DF就是一种二维表数据集,每一列数据都带有列名和数据类型。可以做到对每一列数据进行操作。

4、Spark SQL的架构:

        1、首先是通过DataSource API,是Spark SQL能够支持多种数据源以及数据格式

        2、当不同的数据被加载进来的时候,Spark SQL就是将数据映射成DataFrame,进而被DF API进行处理

        3、基于DF,Spark SQL提供了两种数据处理方式,分别是:

                DSL:是基于DF的一种特殊的语法

                SQL/HQL:标准的SQL或者是Hive SQL

5、常见的Source:

        a、在Spark SQL中也是需要”行为算子“来触发任务的执行

        b、在SparkSQL中行为算子主要分成两种:1、打印    2、保存

                1、打印是不同于RDD需要使用foreach加上println进行打印,在SparkSQL中可以直接使用show方法来对数据进行展示,show方法可以接受三个参数:

                numRows:指定打印的数据条数

                truncate:指定是否将数据截断再进行展示

                vertical:执行是否是水平还是垂直展示数据,默认false表示水平展示

 补充:

                Spark SQL 在打印的时候也可以使用foreach进行打印。

   import org.apache.spark.internal.Logging
import org.apache.spark.sql.{DataFrame, SaveMode, SparkSession}
import org.slf4j.Logger

object Demo02SourceAPICsv extends Logging{
  val logger: Logger = log
  def main(args: Array[String]): Unit = {
    //构建SparkSession
    val spark: SparkSession = SparkSession
      .builder()
      .master("local")
      //.appName("Demo02SourceAPI")
      .appName(this.getClass.getSimpleName.replace("$",""))
      .getOrCreate()


    val csvDF: DataFrame = spark
      .read
      .format("csv")
      .option("seq", ",")
      .schema("id string,name string,age int,gender string,clazz string")
      .load("spark/data/stu/students.txt")





 csvDF.foreach(row=>{
      // Row对象表示带结构的一行数据,可以通过列名获取对应的列值
      val id: String = row.getAs[String]("id")
      val name: String = row.getAs[String]("name")
      val age: Int = row.getAs[Int]("age")
      val gender: String = row.getAs[String]("gender")
      val clazz: String = row.getAs[String]("clazz")
      println(s"$id,$name, $age,$gender,$clazz")
    })

                2、保存:通过调用DF调用write进行保存数据,Spark SQL 在保存数据的时候提供了多种写入方式,可以通过mode使用SaveMode进行指定,SaveMode总共提供了四种不同的写入方式:

                Append:追加

                Ovrewrite:覆盖

                ErrorIfExists:存在即报错

                Ignore:存在即不写入

        c、读文件的一般流程:

                spark:构建的SparkSession环境

                read:表示读取文件,同理写文件即write

                format:指定要读取文件的格式或方式 常见的有:csv、json、parquet、orc、jdbc                 option:设置读取时的参数,KV格式 读csv时,可通过sep设置列分隔符 读jdbc是,需要指定用jdbcURL、户名、密码、数据库、表名等信息

                schema:若数据本身不带结构,则需要通过schema手动指定列名及列的类型 load:加载数据,读文件时需要执行路径

        1、读写csv格式文件:逗号分割文件,常用于读取我文本文件
import org.apache.spark.internal.Logging
import org.apache.spark.sql.{DataFrame, SaveMode, SparkSession}
import org.slf4j.Logger

object Demo02SourceAPICsv extends Logging{
  val logger: Logger = log
  def main(args: Array[String]): Unit = {
    //构建SparkSession
    val spark: SparkSession = SparkSession
      .builder()
      .master("local")
      //.appName("Demo02SourceAPI")
      .appName(this.getClass.getSimpleName.replace("$",""))
      .getOrCreate()


    //1、csv格式:逗号分割文件,常用于读取文本文件
    /**
     * spark:构建的SparkSession环境
     * read:表示读取文件,同理写文件即write
     * format:指定要读取文件的格式或方式常见的有:csv、json、parquet、orc、jdbc
     * option:设置读取时的参数,KV格式,读csv时,可通过sep设置列分隔符,读jdbc是,需要指定用jdbcURL、户名、密码、数据库、表名等信息
     * schema:若数据本身不带结构,则需要通过schema手动指定列名及列的类型
     * load:加载数据,读文件时需要执行路径
     */
    val csvDF: DataFrame = spark
      .read
      .format("csv")
      .option("seq", ",")
      .schema("id string,name string,age int,gender string,clazz string")
      .load("spark/data/stu/students.txt")

    // 同样在SparkSQL中也需要”行为算子“触发任务的执行
    // 在SparkSQL中常用的行为算子一般有两种:1、打印 2、保存

    /**
     * 1、打印:不同于RDD需要使用foreach加上println进行打印
     * 在SparkSQL中可以直接使用show方法来对数据进行展示
     * show方法可以接收三个参数:
     * numRows指定打印的数据条数
     * truncate指定是否将数据截断再进行展示
     * vertical执行是否水平还是垂直展示数据,默认false表示水平展示
     */
      csvDF.show() //默认显示的是二十条数据
      csvDF.show(numRows = 15,truncate=false)
      csvDF.show(numRows = 15,truncate=1,vertical = false)

    /**
     * 使用foreach进行打印
     */
   // csvDF.foreach(row=>{
      // Row对象表示带结构的一行数据,可以通过列名获取对应的列值
    //  val id: String = row.getAs[String]("id")
    //  val name: String = row.getAs[String]("name")
     // val age: Int = row.getAs[Int]("age")
   //   val gender: String = row.getAs[String]("gender")
   //   val clazz: String = row.getAs[String]("clazz")
    //  println(s"$id,$name, $age,$gender,$clazz")
   // })

    /**
     * 2、保存数据:通过DF调用write进行保存
     * Spark SQL在保存的数据的时候提供了多种写入方式,可以通过mode使用SaveMode进行指定
     * SaveMode总共提供了四种不同的写入方式:
     * Append:追加
     * Overwrite:覆盖
     * ErrorIfExists:存在即报错
     * Ignore:存在即不写入
     */

    csvDF
      .write
      .format("csv")
      .option("sep",",")
      .mode(SaveMode.Overwrite)
      .save("spark/data/stu/csv/")

    csvDF
      .write
      .format("json")
      .mode(SaveMode.Overwrite)
      .save("spark/data/stu/json/")


    csvDF
      .write
      .format("orc")
      .mode(SaveMode.Overwrite)
      .save("spark/data/stu/orc/")


    csvDF
      .write
      .format("parquet")
      .mode(SaveMode.Overwrite)
      .save("spark/data/stu/parquet/")





  }
}
        2、读写json格式文件:对于json格式文件,是不需要指定结构,但是在存储的时候比较浪费空间


import org.apache.spark.sql.{DataFrame, SparkSession}

object Demo03SourceAPIJson {
  def main(args: Array[String]): Unit = {
    val spark: SparkSession = SparkSession
      .builder()
      .master("local")
      .appName(this.getClass.getSimpleName.replace("$",""))
      .getOrCreate()

    //对于读取json格式的文件,不需要指定结构,但是在存储时候比较浪费空间空间
    val jsonDF: DataFrame = spark
      .read
      .format("json")
      .load("spark/data/stu/json")
    jsonDF.show()




  }

}
        3、读写orc格式文件:同样在读取的时候是不需要指定结构的,由于使用snappy进行压缩,所以空间利用的比较好,同时也是一种列式存储的格式,对于SQL的查询比较友好。


import org.apache.spark.sql.{DataFrame, SparkSession}
object Demo04SourceAPIOrc {
  def main(args: Array[String]): Unit = {
    val spark: SparkSession = SparkSession
      .builder()
      .master("local")
      .appName(this.getClass.getSimpleName.replace("$", ""))
      .getOrCreate()

    //在使用orc的时候,同样在读取时候不需要指定结构,由于使用了snappy进行压缩,所以空间的利用比较好,
    // 该格式同时也是一种列式存储的一种格式,对于SQL的查询比较友好
    val orcDF: DataFrame = spark
      .read
      .format("orc")
      .load("spark/data/stu/orc/")
    orcDF.show(numRows = 15)
  }




}
        4、读写parquet格式文件:同样在读取的时候时不需要指定特定的结构,同样使用的是snappy进行压缩,相比较orc,该格式更适合存储嵌套格式的数据
import org.apache.spark.sql.{DataFrame, SparkSession}

object Demo05SourceAPIParquet {

  def main(args: Array[String]): Unit = {
    val spark: SparkSession = SparkSession
      .builder()
      .appName(this.getClass.getSimpleName)
      .master("local")
      .getOrCreate()
    //parquet:同样在读取的时候不需要指定结构,同样使用的式snappy进行压缩,
    //相比较orc,该格式更适合存储嵌套格式的数据
    val parquetDF: DataFrame = spark
      .read
      .load("spark/data/stu/parquet")
    parquetDF.show()
  }

}
6、Spark SQL读取和写入MysQL


import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, SaveMode, SparkSession}


object Demo06SourceAPIMysql {
  def main(args: Array[String]): Unit = {
    val spark: SparkSession = SparkSession
      .builder()
      .appName(this.getClass.getSimpleName)
      .master("local")
      //在SparkSql中,如果出现shuffle操作,那么就会默认生成200个切片,即对应200个任务
      //可以通过指定参数来改变切片的数量
      .config("spark.sql.shuffle.partitions","2")
      .getOrCreate()

    /**
     * 1、对于旧的版本来说,在使用MySQl进行写入数据的时候,数据必须要是元组的形式才能toDF,转换成DF形式的数据。
     *
     */

    val MysqlDF: DataFrame = spark
      .read
      .format("jdbc")
      .option("driver","com.mysql.jdbc.Driver")
      .option("url", "jdbc:mysql://master:3306/student?useUnicode=true&characterEncoding=utf8")
      .option("dbtable", "student")
      .option("user", "root")
      .option("password", "123456")
      .load()
    MysqlDF.show()




    //使用隐式转换,作用是将RDD与DF之间的相互转换。
    import  spark.implicits._
    //需求:统计班级的人数,并将数据写入放到Mysql中
    //第一种方式使用RDD的方法:
    //将DataFrame转换成RDD
    val countRDD: RDD[(String, Int)] = MysqlDF
      .rdd
      //再从表中获取
      .map(row => {(row.getAs[String]("clazz"), 1)})
      .reduceByKey((k1, k2) => {k1 + k2})
    //toDF:传入的参数表示的是传入的列名,有多少列传入多少参数
    val WriteDF: DataFrame = countRDD.toDF("clazz", "count")

    //将读取的数据写入到MySQL中
    WriteDF
      .write
      .format("jdbc")
      .option("url", "jdbc:mysql://master:3306/student")
      .option("dbtable", "clazz_cnt_spark")
      .option("user", "root")
      .option("password", "123456")
      .option("truncate","true")//使用truncate,会让重写的时候是删除数据,不删除表,因此就不会改变表的结构
      .mode(SaveMode.Overwrite)//此处的Overwrite会将表删除,然后再按照Spark的映射关系创建表,可能会改变表的结构
      .save()

    /**
     *
     * 修改MySQL默认的编码:
     * vim /etc/my.cnf
     *
     * [client]
     * default-character-set = utf8mb4
     * [mysqld]
     * character-set-server = utf8mb4
     * collation-server = utf8mb4_general_ci
     *
     * 重启MySQL服务:systemctl restart mysqld
     * */


//    //第二种方式:使用spark SQL 的方式,需求:统计每个班级的人数
//    //首先需要将DF注册成一张”表“
//    MysqlDF.createOrReplaceTempView("MySqlTB")
//      spark.sql(
//        """
//          |select
//          |    clazz
//          |    ,count(*) as count_clazz
//          |from
//          |   MySqlTB
//          |group by clazz
//          |""".stripMargin)
//        .show()

    //因为后面返回的数据类型依旧是DF的数据类型,所以后续还可以在进行一系类的操作

  }

}





    /**
     * SQL中常见的操作:
     * 1、基础操作:
     * select  分组字段
     * ,聚合操作
     * ,使用函数对字段进行处理
     * from tb
     * where 过滤
     * group by 分组
     * having 对分组聚合的结果做过滤
     * order by 排序,asc升序、desc降序
     * limit 限制返回的条数
     *
     * 2、多表操作:
     * 2.1 union联接:表与表之间进行纵向(行的方向)的合并
     * union :去重
     * union all:不会去重
     *
     * 2.2 join关联:表与表之间进行横向(列的方向)的合并
     * inner内连接:join / inner join
     * outer外连接:
     * 左外连接:left join / left outer join
     * 右外连接:right join / right outer join
     * 全外连接:full join / full outer join
     *
     * 3、高级操作:
     * 行列转换:
     * 一行转多行:lateral view + UDTF函数
     * 多行转一行:group by + collect_list / collect_set
     * 自定义函数:UDF、UDTF、UDAF(基本不用)
     *
     * 4、函数的使用:
     * 字符串函数:substring、split、concat、concat_ws......
     * 数值函数:round、ceil、floor、abs......
     * 条件函数:if、case when、coalesce
     * 日期时间函数:from_unixtime、unix_timestamp、datediff、date_add......
     * 聚合函数:max、min、sum、count、avg
     * 窗口函数:row_number、rank、dense_rank、lag、lead、max、min、sum、count、avg......
     *
     */
7、Spark  SQL -- DSL的基本操作:

        1、在Spark SQL中的DataFrame的一种特定领域语言

        2、DSL需要基于DataFrame进行操作,所以需要先构造DataFrame

        3、DSL一般支持两种表达方式:

                a、字符串表达式:只有在运行的时候才会知道错误出现的地方

                b、列表达式$"参数名称":(推荐)当出现错误的时候,会自动的提示,但是在使用列表达式的时候需要先导入隐式转换:

import   spark.sql.implicits._


import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}

import scala.tools.nsc.{Main, Variance}

object Demo07DSL {
  def main(args: Array[String]): Unit = {
    //构建sparkSession入口
    val spark: SparkSession = SparkSession
      .builder()
      .appName(this.getClass.getSimpleName)
      .master("local")
      .config("spark.sql.shuffle.partitions","2")
      .getOrCreate()

    //读取文件,学生数据表:
    val stuDF: DataFrame = spark
      .read
      .format("csv")
      .option("sep", ",")
      .schema("id String ,name String,age String,gender String,clazz String")
      .load("spark/data/stu/students.txt")
//      .show()


    /**
     * 1、在使用列式转换的时候需要进行隐式转换
     * 2、在使用函数的时候需要导入函数包
     */
    import spark.implicits._
    import org.apache.spark.sql.functions._

    /**
     * 1、select
     */

    //字符串表达式:
    stuDF
      .select("id","name","gender","clazz")
//      .show()
    //列表达式:
    stuDF
      .select($"id",$"name",$"clazz")
//      .show()
    //需求:使用函数将文科和理科取出来
    stuDF
      .select($"id", $"name", substring($"clazz", 1, 2))
//      .show()


    /**
     * 需求:对每个班级中的学生按照学号进行降序排序
     * 使用开窗函数
     * ,row_number() over Window.partitionBy($"clazz").orderBy($"id".desc) as "rn")
     *
     */
    stuDF
      .select($"name",$"age",$"gender",row_number() over Window.partitionBy($"clazz").orderBy($"id".desc) as "rn")
//      .show()

    /**
     * 需求:使用where过滤出来班级是理科三班的同学
     * 在Spark中的===是做等值
     */

    stuDF
      .where($"clazz"==="理科三班")
      .select($"id",$"name",$"gender",$"age")
//      .show()


    /**
     * 需求找出班级是理科三班的男同学
     * 方式一:使用where,使用 and 接受两个过滤
     * 方式二:使用filter,使用RDD的方式进行过滤
     */
    stuDF
      .where($"clazz" === "理科三班" and $"gender"==="男")
      .select($"id", $"name", $"gender", $"age",$"clazz")
//      .show()

    stuDF
      .filter(row=>{
        var flag = false
        if(row.getAs[String]("clazz")=="理科三班"  && row.getAs[String]("gender")=="男")
        {
          flag = true
        }
        flag
      })
//      .show()


    /**
     * 分组 聚合
     * 需求:统计班级的人数
     * 1、agg:表示的是聚合的意思
     * 2、count:表示的是统计的数量
     * 3、countDistinct:表示的是对统计的数量进行去重
     */


    stuDF
      .groupBy($"clazz")
      .agg(count($"id"))
//      .show()

    //需求:统计班级的人数,并按照人数降序排序,返回前三条数据
    stuDF
    .groupBy($"clazz")
      .agg(countDistinct($"name") as "cnt")
      .orderBy($"cnt" desc)
      .limit(3)
//      .show()


    /**
     *使用关联
     * 对于相同的关联字段的解决方式,因为使用相同的关联字段进行关联的时候会产生ambigious错误:
     * 1、对其中某一个DF中的关联字段更改名称,可以使用withColumnRename改变名称
     * 2、换一种方式进行关联
     */


    //读取学生的分数表
    val scoreDF: DataFrame = spark
      .read
      .format("csv")
      .option("seq", ",")
      .schema("id String, subject_id String,score  Int")
      .load("spark/data/stu/score.txt")
//      .show()


    //  inner join   第一种join方式:
    scoreDF
      .withColumnRenamed("id","sid")
      .join(stuDF,$"id"===$"sid")
//      .show()

    //第二种join方式,通过字符串指定,但是无法指定关联的方式,默认使用的是 inner join:
    scoreDF
      .join(stuDF,"id")
//      .show()

    //第三种join的方式,使用list进行关联,可以指定join的方式,当有多个关联的字段,可以在List中添加:
//    scoreDF
      .join(stuDF,List("id"),"inner")
//      .show()
    //outer join
    scoreDF
      .join(stuDF,List("id"),"left")
//      .show()

    scoreDF
      .join(stuDF, List("id"), "right")
//      .show()

    scoreDF
      .join(stuDF, List("id"), "full")
//      .show()


    //union纵向关联
    //对于union来说类似于union all,不会对数据进行去重,可以通过使用distinct来进行对数据的去重
    val stuDF01: Dataset[Row] = stuDF.sample(false, 0.01, 1)
    val stuDF02: Dataset[Row] = stuDF.sample(false, 0.01, 1)
    stuDF01.union(stuDF02)
//      .show()

    /**
     * 行列转换
     */
    //一行转多行

    val lineDF: DataFrame = spark
      .read
      .format("csv")
      .option("sep", "#")
      .schema("line String")
      .load("spark/data/words.txt")

    lineDF
      .select(explode(split($"line",","))  as "clo")
//      .show()


    val line1DF: DataFrame = spark
      .read
      .format("csv")
      .option("sep", " ")
      .schema("id string,name string,weight string")
      .load("spark/data/stu/weight.txt")
//    line1DF.show()

    val newLineDF: DataFrame = line1DF
      .select($"id", $"name", explode(split($"weight", ",")) as "weight_count")
//      .show()

    //多行转一行,可以使用的是GroupBy,结合collect_list或者是collect_set进行聚合压缩
    newLineDF
      .groupBy($"id",$"name")
      //此时输出的结果是一个[150, 180, 170, 200],可以使用一个concat_ws()可以转成string的类型
      .agg(concat_ws(",",collect_list($"weight_count")))
//      .show()


    //快速需求:每个班级的总分前三名
    scoreDF
      .groupBy($"id")
      .agg(sum($"score") as "sum_score")
      .join(stuDF,List("id"))
      .select($"id",$"name",$"clazz",$"sum_score")
      //使用withColumn可以添加一列
      .withColumn("rn",row_number() over Window.partitionBy($"clazz").orderBy($"sum_score".desc))
      .where($"rn"<=3)
      .show()














  }

}
8、RDD与DF之间的相互转化

import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}

object Demo08RDDToDF {
  case class Student(id:String,name:String,age:String,gender:String,clazz:String)

  def main(args: Array[String]): Unit = {


    //构建sparkSession入口
    val spark: SparkSession = SparkSession
      .builder()
      .appName(this.getClass.getSimpleName)
      .master("local")
      .config("spark.sql.shuffle.partitions", "2")
      .getOrCreate()

    import  spark.implicits._
    import  org.apache.spark.sql.functions._
    /**
     * RDD与DF之间的相互转化
     */
    val stuDF: DataFrame = spark
      .read
      .format("csv")
      .option("seq", ",")
      .schema("id String,name String, age Int,gender String,clazz String")
      .load("spark/data/stu/students.txt")
//      .show()

    /**
     * 将DF转化成RDD,并完成需求:统计班级是文科三班并且是女生的同学
     */

    //方式一:直接rdd进行转化
    stuDF
      .rdd
      .filter(row=>{
        var flag :Boolean=false
        if(row.getAs[String]("clazz")=="文科三班"  &&  row.getAs[String]("gender")=="女"){
          flag=true
        }
        flag
      }).foreach(println)


    //方式二:使用as的方式直接指定数据的类型,再通过模式匹配获取数据
    val DFToRDD01: Dataset[(String, String, Int, String, String)] = stuDF
      .as[(String, String, Int, String, String)]
    val RDD01: RDD[(String, String, Int, String, String)] = DFToRDD01
      .rdd
    RDD01
      .map {
      case (id: String, name: String, age: Int, gender: String, clazz: String) =>
        s"$id,$name,$age"
    }.foreach(println)


    //RDD 转化成 DF

    //第一种方式使用toDF进行转换,使用之前需要注意的是需要导入函数的包
    //第一步:构造一个RDD
    val stuRDD: RDD[String] = spark.sparkContext.textFile("spark/data/stu/students.txt")
    val stuRDD01: RDD[(String, String, String, String)] = stuRDD.map(kv => {
      val id: String = kv.split(",")(0)
      val name: String = kv.split(",")(1)
      val age: String = kv.split(",")(2)
      val gender: String = kv.split(",")(3)
      (id, name, age, gender)
    })
    stuRDD01.toDF("id","name","age","gender")
      .show()

//第二种方式:通过样例类对象进行DF的转换,就是创建一个对象,将获取去的数据封装到类中,再通对象调用toDF的方法进行转换

    val RDDToDF: DataFrame = stuRDD.map(kv => {
      Student(kv.split(",")(0), kv.split(",")(1), kv.split(",")(2), kv.split(",")(3), kv.split(",")(4))
    })
      //此处不需要指定参数,原因是可以自动的从样例类对象中自动的解析
      .toDF()
    RDDToDF.show()
  }
}
9、SparkSQL 整合hive  使用hive的元数据


在hive的hive-site.xml修改一行配置

  在使用hive或Spark-SQL等之前都需要先启动元数据服务已经修改过可以跳过此步骤

  <property>
    <name>hive.metastore.uris</name>
    <value>thrift://master:9083</value>
  </property>
 

 将hive-site.xml  复制到spark conf目录下
  cp /usr/local/soft/hive-3.1.2/conf/hive-site.xml /usr/local/soft/spark-3.1.3/conf/

启动hive元数据服务

  hive --service metastore(推荐 更方便查看日志 随启随用)

  或

   nohup hive --service metastore >> metastore.log 2>&1 &

将mysql 驱动包复制到spark jars目录下

  cp /usr/local/soft/hive-3.1.2/lib/mysql-connector-java-5.1.49.jar /usr/local/soft/spark-3.1.3/jars/

整合好之后在spark-sql 里面就可以使用hive的表了 

  不能使用yarn-cluster模式

  spark-sql --master yarn --deploy-mode client --conf  spark.sql.shuffle.partitions=2

 在spark-sql中设置运行参数

    set spark.sql.shuffle.partitions=2;

 10、Spark  SQL在本地连接Hive

        1、准备工作:

                a、导入依赖:

<dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-hive_2.12</artifactId>
            <version>3.1.3</version>
        </dependency>

                b、将hive-site.xml的配置文件复制到Idea中的Source中

 

         2、运行实例:



object Demo10SparkToHive {
  def main(args: Array[String]): Unit = {
    import org.apache.spark.sql.expressions.Window
    import org.apache.spark.sql.{DataFrame, SparkSession}

    object Demo08SparkOnHive {
      def main(args: Array[String]): Unit = {

        val spark: SparkSession = SparkSession
          .builder()
          .appName(this.getClass.getSimpleName.replace("$", ""))
          .master("local")
          .config("spark.sql.shuffle.partitions", "2")
          .enableHiveSupport() // 开启Hive的支持
          .getOrCreate()

        /**
         * 通过enableHiveSupport()可以开启Hive的支持
         * 需要在pom文件中加入spark-hive的依赖
         * 还需要将hive-site.xml放入当前模块的resources目录下
         * <dependency>
         * <groupId>org.apache.spark</groupId>
         * <artifactId>spark-hive_2.12</artifactId>
         * <version>3.1.3</version>
         * </dependency>
         *
         */

        spark.sql("use default")

        spark.sql(
          """
            |select  tt1.id
            |        ,tt1.name
            |        ,tt1.clazz
            |        ,tt1.sum_score
            |        ,tt1.rn
            |from (
            |    select  t1.id
            |            ,t1.name
            |            ,t1.clazz
            |            ,t2.sum_score
            |            ,row_number() over(partition by t1.clazz order by t2.sum_score desc) as rn
            |    from student t1
            |    join (
            |        select  student_id as id
            |                ,sum(sco) as sum_score
            |        from score
            |        group by student_id
            |    ) t2 on t1.id = t2.id
            |) tt1 where tt1.rn <= 3
            |""".stripMargin).show()

        // 将Hive中的表转换成DF
        val stuDF: DataFrame = spark.table("student")
        val scoDF: DataFrame = spark.table("score")

        import spark.implicits._
        import org.apache.spark.sql.functions._

        scoDF
          .groupBy($"student_id")
          .agg(sum($"sco") as "sum_score")
          .join(stuDF, $"student_id" === $"id", "inner")
          .withColumn("rn", row_number() over Window.partitionBy($"clazz").orderBy($"sum_score".desc))
          .where($"rn" <= 3)
          .show()


      }

    }
  }

}
总结:Spark  SQL 写代码的几种方式:



1、在IDEA中将代码编写好打包上传到集群中运行(上线使用)

​    使用spark-submit提交

 2、spark-shell 

(repl) 里面使用sqlContext   测试使用,简单任务使用

spark-shell --master yarn --deploy-mode client

只能使用默认的local模式或者yarn-client模式,不能使用yarn-cluster

3、spark-sql

spark-sql --master yarn --deploy-mode client

只能使用默认的local模式或者yarn-client模式,不能使用yarn-cluster

        

                

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值