大数据技术之Spark SQL

大数据技术之Spark SQL

一:Spark SQL的概述

  1. 定义:Spark SQL是Spark用来处理结构化数据的一个模块,它提供了2个编程对象:DataFrame和DataSet,并且作为分布式SQL查询引擎的作用。

  2. 特点:易整合、统一的数据访问方式、兼容Hive、标准的数据连接

  3. DtaFrame的定义:与RDD类似,DataFrame也是一个分布式的数据容器。然而Dataframe更像是一个数据库的二维表格,除了有数据以外,还记录着数据的结构信息,即schema

  4. DataSet的定义:DataSet是DtaFrame API的一个扩展,是Spark最新的数据抽象。

    1) 是 Dataframe API 的一个扩展,是 Spark 最新的数据抽象。
    2) 用户友好的 API 风格,既具有类型安全检查也具有 Dataframe 的查询优化特性。
    3) Dataset 支持编解码器, 当需要访问非堆上的数据时可以避免反序列化整个对象,提高了
    效率。
    4) 样例类被用来在 Dataset 中定义数据的结构信息,样例类中每个属性的名称直接映射到
    4 / 26 北京东燕郊开发区燕灵路方舟广场南侧 169 号 电话: 010-83868569
    DataSet 中的字段名称。
    5) Dataframe 是 Dataset 的特列, DataFrame=Dataset[Row] ,所以可以通过 as 方法将 Dataframe
    转换为 Dataset。 Row 是一个类型,跟 Car、 Person 这些的类型一样,所有的表结构信息我都用
    Row 来表示。
    6) DataSet 是强类型的。比如可以有 Dataset[Car], Dataset[Person].
    7) DataFrame 只是知道字段,但是不知道字段的类型,所以在执行这些操作的时候是没办法
    在编译的时候检查是否类型失败的,比如你可以对一个 String 进行减法操作,在执行的时候才报
    错,而 DataSet 不仅仅知道字段,而且知道字段类型,所以有更严格的错误检查。 就跟 JSON 对
    象和类对象之间的类比

二:Spark SQL 编程

​ 在老版本中,SparkSQL 提供两种 SQL 查询起始点: 一个叫 SQLContext,用于 Spark 自己提供的 SQL 查询; 一个叫 HiveContext,用于连接 Hive 的查询。

​ SparkSession 是Spark最新的 SQL 查询起始点,实质上是 SQLContext 和 HiveContext 的组合,所以在 SQLContext 和 HiveContext 上可用的 API 在 SparkSession 上同样是可以使用的。SparkSession 内部封装了 sparkContext,所以计算实际上是由 sparkContext 完成的。

  1. DataFrame: 在 Spark SQL 中 SparkSession 是创建 DataFrame 和执行 SQL 的入口,创建 DataFrame 有三种方式: 通过 Spark 的数据源进行创建; 从一个存在的 RDD 进行转换; 还可以从 Hive Table 进行查询返回。

    1)数据源创建

    1. 查看Spark数据源进行创建的文件格式
    spark.read
    2. 读取json文件创建DataFrame
    val df=spark.read.json("/opt/module/spark/examples/src/main/resources/people.json")
    

    2)从RDD进行转换创建

    3)从Hive Table进行查询返回创建

    后续第三章着重介绍

  2. SQL的语法风格

    1. 创建一个DataFrame
    val df=spark.read.json("/opt/module/spark/examples/src/main/resources/people.json")
    2.对 DataFrame 创建一个临时表
    scala> df.createOrReplaceTempView("people")
    注意: 临时表是 Session 范围内的, Session 退出后,表就失效了。如果想应用范围内有效,可以
    使用全局表。注意使用全局表时需要全路径访问, 如: global_temp.people
    3. 对于 DataFrame 创建一个全局表
    scala> df.createGlobalTempView("people")
    4. 通过 SQL 语句实现查询全表
    scala> spark.sql("SELECT * FROM global_temp.people").show()
    scala> spark.newSession().sql("SELECT * FROM global_temp.people").show()
    
  3. RDD转换为DataFrame

    1. 注意: 如果需要 RDD 与 DF 或者 DS 之间操作,那么都需要引入 import spark.implicits._ 【 spark

      不是包名,而是 sparkSession 对象的名称】
      前置条件: 导入隐式转换并创建一个 RDD 。

    scala> import spark.implicits._
    import spark.implicits._
    scala> val peopleRDD = sc.textFile("examples/src/main/resources/people.txt")
    peopleRDD: org.apache.spark.rdd.RDD[String] = examples/src/main/resources/people.txt MapPartitionsRDD[3] at
    textFile at <console>:27
    1)通过手动确定转换
    scala> peopleRDD.map{x=>val para = x.split(",");(para(0),para(1).trim.toInt)}.toDF("name","age")
    
  4. DataFrame转换为RDD

    1. 直接调用rdd即可

      1) 创建一个 DataFrame
      scala> val df = spark.read.json("/opt/module/spark/examples/src/main/resources/people.json")
      2)将 DataFrame 转换为 RDD
      scala> val dfToRDD = df.rdd
      

三:DataSet:DataSet是具有强类型的数据集合,需要提供对应的类型信息

  1. 创建DataSet

    1. 创建一个样例类
    case class Person(nam: String,age:Long)
    2. 创建 DataSet
    val caseClassDS = Seq(Person("Andy", 32)).toDS()
    
    1. RDD转换为DataSet

    SparkSQL 能够自动将包含有 case 类的 RDD 转换成 DataFrame, case 类定义了 table 的结构,
    case 类属性通过反射变成了表的列名。

    1. 创建一个 RDD
    val peopleRDD = sc.textFile("examples/src/main/resources/people.txt")
    2. 创建一个样例类
    case class Person(name: String, age: Long)
    3. 将 RDD 转化为 DataSet
    scala> peopleRDD.map(line => {val para = line.split(",");Person(para(0),para(1).trim.toInt)}).toDS()
    
    1. DataSet转换为RDD:调用rdd方法即可
    1) 创建一个 DataSet
    scala> val DS = Seq(Person("Andy", 32)).toDS()
    2)将 DataSet 转换为 RDD
    scala> DS.rdd
    
    1. DataFrame互相转换为Data’’Set
    1)创建一个 DateFrame
    scala> val df = spark.read.json("examples/src/main/resources/people.json")
    df: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
    2)创建一个样例类
    scala> case class Person(name: String, age: Long)
    defined class Person
    3)将 DateFrame 转化为 DataSet
    scala> df.as[Person]
    res14: org.apache.spark.sql.Dataset[Person] = [age: bigint, name: string]
    2. DataSet 转换为 DataFrame
    1)创建一个样例类
    scala> case class Person(name: String, age: Long)
    defined class Person
    2)创建 DataSet
    scala> val ds = Seq(Person("Andy", 32)).toDS()
    ds: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]
    3)将 DataSet 转化为 DataFrame
    scala> val df = ds.toDF
    

    总结:DataSet转换DataFrame的方法很简单,只需要把case class封装成ROW即可,导入隐式转换后调用toDF。DataFrame转DataSet也比较简单,只需要导入隐式转换后调用as【row】方法,将其具体数据类型标注清除即可

  2. 三者共性

    1、 RDD、 DataFrame、 Dataset 全都是 spark 平台下的分布式弹性数据集,为处理超大型数据
    提供便利
    2、三者都有惰性机制,在进行创建、转换,如 map 方法时,不会立即执行,只有在遇到 Action
    如 foreach 时,三者才会开始遍历运算。
    3、三者都会根据 spark 的内存情况自动缓存运算,这样即使数据量很大,也不用担心会内存
    溢出。
    4、三者都有 partition 的概念
    5、三者有许多共同的函数,如 filter,排序等
    6、在对 DataFrame 和 Dataset 进行操作许多操作都需要这个包进行支持
    import spark.implicits._
    7、 DataFrame 和 Dataset 均可使用模式匹配获取各个字段的值和类型

  3. 三者区别

    1.RDD:

    ​ 1) RDD 一般和 spark mlib 同时使用
    ​ 2) RDD 不支持 sparksql 操作

    2.DataFrame:

    ​ 1) 与 RDD 和 Dataset 不同, DataFrame 每一行的类型固定为 Row, 每一列的值没法直接访问, 只有通过解析才能获取各个字段的值,

    ​ 2) DataFrame 与 Dataset 一般不与 spark mlib 同时使用
    ​ 3) DataFrame 与 Dataset 均支持 sparksql 的操作,比如 select, groupby 之类,还能注册临时表/视窗,进行 sql 语句操作

    ​ 4) DataFrame 与 Dataset 支持一些特别方便的保存方式,比如保存成 csv,可以带上表头,这样每一列的字段名一目了然

    3.DataSet:

    ​ 1) Dataset 和 DataFrame 拥有完全相同的成员函数,区别只是每一行的数据类型不同。
    ​ 2) DataFrame 也可以叫 Dataset[Row],每一行的类型是 Row,不解析,每一行究竟有哪些字段,各个字段又是什么类型都无从得知,只能用上面提到的 getAS 方法或者共性中的第七条提到的模式匹配拿出特定字段。 而 Dataset 中,每一行是什么类型是不一定的,在自定义了 case class之后可以很自由的获得每一行的信息

    可以看出, Dataset 在需要访问列中的某个字段时是非常方便的,然而,如果要写一些适配性
    很强的函数时,如果使用 Dataset,行的类型又不确定,可能是各种 case class,无法实现适配, 这时候用 DataFrame 即 Dataset[Row]就能比较好的解决问题 。

  4. IDEA创建SparkSQL以及用户自定义UDF

    package com.ityouxin.sparkSql
    
    import org.apache.spark.sql.SparkSession
    
    object HelloWorld {
      def main(args: Array[String]): Unit = {
        //创建 SparkConf()并设置 App 名称
        val spark = SparkSession
          .builder()
          .master("local[*]")
          .appName("Spark SQL basic example")
          .config("spark.some.config.option", "some-value")
          .getOrCreate()
        import  spark.implicits._
        val df = spark.read.json("datas/people.json")
        df.show()
        df.filter($"age" > 21).show()
        df.createOrReplaceTempView("persons")
        spark.sql("SELECT * FROM persons where age > 21").show()
        spark.stop()
      }
    }
    

四:Spark SQL的数据源

  1. 通用加载/保存方法

    1. 手动指定选项

      Spark SQL 的 DataFrame 接口支持多种数据源的操作。一个 DataFrame 可以进行 RDDs 方式的操作,也可以被注册为临时表。把 DataFrame 注册为临时表之后,就可以对该 DataFrame 执行SQL 查询。

      Spark SQL 的默认数据源为 Parquet 格式。数据源为 Parquet 文件时, Spark SQL 可以方便的执行所有的操作。修改配置项 spark.sql.sources.default,可修改默认数据源格式。

      当数据源格式不是 parquet 格式文件时,需要手动指定数据源的格式。数据源格式需要指定全名(例如: org.apache.spark.sql.parquet),如果数据源格式为内置格式,则只需要指定简称定json, parquet, jdbc, orc, libsvm, csv, text 来指定数据的格式。可以通过 SparkSession 提供的 read.load 方法用于通用加载数据,使用 write 和 save 保存数据。

      val peopleDF = spark.read.format("json").load("examples/src/main/resources/people.json")
      val sqlDF = spark.sql("SELECT * FROM parquet.`hdfs://hadoop102:9000/namesAndAges.parquet`")
      
    2. 文件保存选项

      可以采用 SaveMode 执行存储操作, SaveMode 定义了对数据的处理模式。需要注意的是,这些保存模式不使用任何锁定,不是原子操作。此外,当使用 Overwrite 方式执行时,在输出新数据之前原数据就已经被删除。 SaveMode 详细介绍如下表: 略

    3. JSON

    Spark SQL 能够自动推测 JSON 数据集的结构,并将它加载为一个 Dataset[Row]. 可以通过SparkSession.read.json()去加载一个 一个 JSON 文件。
    注意: 这个 JSON 文件不是一个传统的 JSON 文件,每一行都得是一个 JSON 串。

    val path = "examples/src/main/resources/people.json"
    val peopleDF = spark.read.json(path)
    peopleDF.printSchema()
    
    1. Parquet 文件

    Parquet 是一种流行的列式存储格式,可以高效地存储具有嵌套字段的记录。 Parquet 格式经常在Hadoop 生态圈中被使用,它也支持 Spark SQL 的全部数据类型。 Spark SQL 提供了直接读取和存储 Parquet 格式文件的方法。

    importing spark.implicits._
    import spark.implicits._
    val peopleDF = spark.read.json("examples/src/main/resources/people.json")
    peopleDF.write.parquet("hdfs://hadoop102:9000/people.parquet")
    val parquetFileDF = spark.read.parquet("hdfs:// hadoop102:9000/people.parquet")
    parquetFileDF.createOrReplaceTempView("parquetFile")
    val namesDF = spark.sql("SELECT name FROM parquetFile WHERE age BETWEEN 13 AND 19")
    namesDF.map(attributes => "Name: " + attributes(0)).show()
    
    1. JDBC

    Spark SQL 可以通过 JDBC 从关系型数据库中读取数据的方式创建 DataFrame,通过对DataFrame 一系列的计算后,还可以将数据再写回关系型数据库中。
    注意:需要将相关的数据库驱动放到 spark 的类路径下。

    /load 默认的加载文件的格式由参数spark.sql.source.default决定
        val df = spark.read.load("datas/users.parquet")
        df.show()
       // spark.sql("select * from parquet.'datas/users.parquet'").show()
        //spark.sql("select * from json.'datas/people.json'").show()
      //jdbc
        spark.read.format("jdbc")
          .option("url","jdbc:mysql://localhost:3306/day10")
          .option("dbtable","user")
          .option("user","root")
          .option("password","123")
          .load().show()
        println("-------------------")
        val option = new Properties
        option.setProperty("user","root")
        option.setProperty("password","123")
        spark.read.jdbc("jdbc:mysql://localhost:3306/day10","user",option).show()
    
    1. Hive

    Apach Hive 是 Hadoop 上的 SQL 引擎, Spark SQL 编译时可以包含 Hive 支持,也可以不包
    含。包含 Hive 支持的 Spark SQL 可以支持 Hive 表访问、 UDF(用户自定义函数)以及 Hive 查询
    语言(HiveQL/HQL)等。需要强调的一点是,如果要在 Spark SQL 中包含 Hive 的库,并不需要事
    先安装 Hive。一般来说,最好还是在编译 Spark SQL 时引入 Hive 支持,这样就可以使用这些特
    性了。如果你下载的是二进制版本的 Spark,它应该已经在编译时添加了 Hive 支持。

发布了82 篇原创文章 · 获赞 6 · 访问量 1505

猜你喜欢

转载自blog.csdn.net/weixin_38255444/article/details/104235627