Spark:SQL(一)

目录

01:上篇回顾

02:学习目标

03:数据结构抽象:设计

04:数据结构抽象:区别与联系

05:数据结构抽象:Row类型

06:数据结构抽象:关系转换

07:数据结构抽象:反射构建

08:数据结构抽象:自定义Schema

09:DSL与SQL分析

10:电影评分案例:数据与需求分析

11:电影评分案例:SQL实现

12:电影评分案例:DSL实现

13:SaveMode与Shuffle分区数

14:DataSet的设计

15:数据源Source:设计

17:数据源Source:Parquet

18:数据源Source:JSON

19:数据源Source:CSV

20:数据源Source:MySQL

附录一:SparkSQL Maven依赖


01:上篇回顾

https://blog.csdn.net/m0_57498038/article/details/119110609

  1. Spark读写Hbase如何实现?

    • 方式:调用Hadoop的InputFormat和OutputFormat

    • 方法

      • 读Hbase:newAPIHadoopRDD【TableInputFormat】

        • 返回值

        • K:ImmutableBytesWritable:Rowkey

        • V:Result:这个Rowkey的数据

        • 注意:特殊读取,需要实现单独配置序列化方式

      • 写Hbase:saveAsNewAPIHadoopFile【TableOutputFormat】

        • 要求:RDD中的数据类型必须为二元组类型,Value必须为Put类型

  2. 共享变量有几种?分别有什么功能?

    • 广播变量:将Driver中的一个对象发送给每个Executor

      • 避免每个Task都需要使用这个变量,都向Driver请求一份

      • 做了广播变量以后,每个Task只要从Executor获取变量即可

      • 减少了网络IO传输,提高性能

      • 使用

        val broad = sc.broadcast(变量)
        //取出
        broad.value
    • 累加器:实现分布式计数

      • 每个分区内部先计数,再将计数的结果进行分区间的合并

  3. 什么是宽依赖和窄依赖,有什么区别?

    • 功能:用于描述父RDD的数据如何分配给子RDD

    • 宽依赖:SHuffle依赖,肯定会产生shuffle

      • 父RDD的一个分区的数据给了子RDD的多个分区

      • 一对多

    • 窄依赖:不会产生shuffle

      • 父RDD的一个分区的数据给了子RDD的一个分区

      • 一对一

  4. SparkSQL的功能、特点和应用场景是什么?

    • 功能:SparkSQL是Spark中专门处理结构化数据的模块

    • 特点

      • 集成的

        • SQL:主要实现类SQL的开发

        • DSL:结合了函数式编程和SQL的特点:将SQL关键字变成了函数:DSL函数 + RDD函数

      • 数据源接口非常丰富:封装了常用的所有数据源的接口:文件/数据库

      • 与Hive的集成:所有Hive所有的开发方式,SparkSQL是全部兼容

      • 标准的数据接口:SQL、JAR、JDBC

    • 应用

      • 离线计算:代替SparkCore:SQL + DSL,访问Hive数据仓库中的数据进行处理

      • 实时计算:StructStreaming,用Spark实现实时计算

  5. SparkSQL的代码如何开发?

    • IDEA中开发:类似于开发SparkCore中

    • 驱动接口和数据抽象

      • SparkCore:SparkContext + RDD

        • 将所有数据读取进来以后,全部放入一个分布式集合【数据】

        • 调用集合函数来进行处理

          rdd.filter(line => line.split("\t")[2] != null)
              .map(line => {
                  val arr = line.split("\t")
                  (arr(0),arr(1))
              })
      • SparkSQL:SparkSession + DataSet / DataFrame

        • 将所有数据读取进来以后,全部放入一个分布式的表【数据 + Schema】

        • 调用DSL函数或者SQL语句来对表的数据进行处理

          ds.where($"thrid" is not null)
              .select($"first",$"second")
    • 代码开发

      //step1:先构建SparkSession对象
      val spark = SparkSession
          .builer
          .master
          .appName
          .config
          .getOrCreate()
          
      //step2:实现处理逻辑
      ​
      //step3:释放资源
      spark.stop
    • DSL:函数式编程

      • 与RDD的编程方式基本类似

      • 区别:调用函数不一样

        • DSL函数:SQL关键字函数:select、where、groupBy、orderBy、limit、agg

        • +

        • RDD函数:结构化数据处理的函数:map、flatMap、filter

    • SQL:基于SQL进行处理

      • step1:将DS或者DF注册为视图

      • step2:使用SQL对视图进行处理

  6. 疑问自答

    //过滤空行 
    .filter(line => line != null && line.trim.length > 0) 
    • 这段代码前半段不是已经过滤了值为null的空行了吗?加个长度不为0的行这样不是会重复过滤吗,是有什么不一样吗?

    • line != null:过滤null

      String = null : line != null
    • line.trim.length > 0:过滤空行

      String = “” : line.trim.length > 0

    • length(word) > 0:过滤空白符

      两个单词之间有两个分隔符:会出现一个空的元素
    • StringUtils.isNotBlank():专门用于过滤空白符的方法

02:学习目标

  1. ==Spark中数据抽象==

    • 什么是DataFrame和DataSet?

    • 与RDD的区别是什么?

    • 怎么实现三者之间的互相转换?

  2. SparkSQL的应用案例

    • 练习DSL和SQL的开发

    • 保存模式:SaveMode

    • Shuffle分区问题

  3. SparkSQL支持的数据源:Source

    • 文本:Parquet、CSV、JSON……

    • JDBC:MySQL

03:数据结构抽象:设计

  • 目标掌握SparkCore与SparkSQL是数据抽象设计

  • 实施

    • SparkCore的设计

      • 设计:将所有数据放入分布式数据集合,调用集合的转换函数来实现处理,返回新的结果集合

      • 实现

        • 数据抽象:RDD【分布式集合:数据】

        • 数据驱动:SparkContext

      • 场景:使用函数式编程对各种数据源的数据进行分布式的计算处理

      • 问题:RDD中只有数据,没有数据的Schema,SparkCore处理结构化数据不如SQL更方便直观

    • SparkSQL的设计

      • 设计:将所有数据放入分布式数据表中,使用SQL或者DSL函数来实现处理,返回新的数据表

      • 实现

        • 数据抽象:DataSet / DataFrame 【分布式表:数据 + Schema】

        • 数据驱动:SparkSession

      • 场景:使用SQL或者DSL对结构化数据源的数据进行分布式的计算处理

  • 小结

    • 掌握SparkCore与SparkSQL是数据抽象设计

04:数据结构抽象:区别与联系

  • 目标掌握RDD、DataFrame、DataSet三者之间的区别于联系

  • 路径

    • step1:数据抽象的设计

    • step2:关系

    • step3:测试

  • 实施

    • 数据抽象的设计

      • RDD:SparkCore中的分布式集合,用于存储数据

      • DataFrame:早期的SparkSQL中分布式表的设计

      • DataSet:后期SparkSQL中分布式表的设计

        • 1.6版本开始引用,保留了DataFrame

        • 2.0版本开始,合并了为DataSet,DataFrame作为DataSet的一种特殊形式

    • 关系

      • RDD:数据,支持泛型

        • RDD【Int】、RDD【String】 =》 RDD【T】 =》 类似于List【T】

      • DF:数据 + Schema,不支持泛型

        • DF【Row】:不可变

      • DS:数据 + Schema,支持泛型

        • DS【Int】、DS【String】 =》 DS【T】 =》 类似于List【T】

    • 测试

      • 先上传数据

        hdfs dfs -mkdir /datas/
        
        cd /export/server/spark
        
        hdfs dfs -put examples/src/main/resources /datas/

    • 启动sparkShell

      bin/spark-shell --master local[2]
    • 构建RDD

      val inputRdd = sc.textFile("/datas/resources/people.json")
    • 构建DF

      val inputDF = spark.read.json("/datas/resources/people.json")
      • 构建DS

        val inputDS = spark.read.textFile("/datas/resources/people.json")

          

        • DataFrame = DataSet[Row]

        • SparkSQL会自动根据读写类型接口去解析文件,获取Schema

        • 如果没有指定接口:整体作为一列Schema,value

        •  

  • 小结

    • RDD、DataFrame、DataSet三者之间的关系是什么?

      • RDD:RDD【T】:数据 + 支持泛型

      • DataFrame:DataFrame:数据 + Schema

        • 没有泛型,固定为Row类型

        • DataFrame = DataSet[Row]

      • DataSet:DataSet【T】 :数据 + Schema + 泛型

05:数据结构抽象:Row类型

  • 目标掌握SparkSQL中DataFrame的Row类型的使用

  • 路径

    • step1:功能

    • step2:创建

    • step3:取值

  • 实施

    • 功能

      • SparkSQL专门用于实现DataFrame数据类型存储的类型对象

    • 取值

      • 从DF中获取Row对象

        scala> val row = inputDF.first
        row: org.apache.spark.sql.Row = [null,Michael]
      • 从Row对象中取出数据

        //不建议:返回类型为Any
        scala> row.get(1)
        res1: Any = Michael
        //不建议:返回类型比较固定
        scala> row.getString(1)
        res2: String = Michael
        //建议使用以下两种方式
        scala> row.getAs[String](1)
        res3: String = Michael
        ​
        scala> row.getAs[String]("name")
        res4: String = Michael
    • 创建

       * // Create a Row from values.
       * Row(value1, value2, value3, ...)
       * // Create a Row from a Seq of values.
       * Row.fromSeq(Seq(value1, value2, ...))
  • 小结

    • 掌握SparkSQL中DataFrame的Row类型的使用

06:数据结构抽象:关系转换

  • 目标:了解RDD、DataFrame、DataSet之间的转换关系

  • 路径

    • step1:转换关系

    • step2:DF/DS转RDD

    • step3:RDD转DF/DS

  • 实施

    • 转换关系

    • DF/DS转RDD

      scala> inputDF.rdd
      res5: org.apache.spark.rdd.RDD[org.apache.spark.sql.Row] = MapPartitionsRDD[14] at rdd at <console>:26
      ​
      scala> inputDS.rdd
      res6: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[19] at rdd at <console>:26
      ​
      scala> inputDF.schema
      res7: org.apache.spark.sql.types.StructType = StructType(StructField(age,LongType,true), StructField(name,StringType,true))
      ​
      scala> inputDS.schema
      res8: org.apache.spark.sql.types.StructType = StructType(StructField(value,StringType,true))
      • 函数

        • .rdd:从DS或者DF取出RDD

        • .schema:从DS或者DF取出Schema

    • RDD转DF/DS

      Spark SQL supports two different methods for converting existing RDDs into Datasets. 
      The first method uses reflection to infer the schema of an RDD that contains specific types of objects. 
      the second method for creating Datasets is through a programmatic interface that allows you to construct a schema and then apply it to an existing RDD. 
      • 方式一:反射方式

        • 将RDD中元素的类型更换为一个拥有Schema的类型:样例类【属性】

      • 方式二:自定义Schema

        • 根据RDD的数据内容,单独定义一个Schema对象:StructType(StructField...)

        • 将RDD与Schema合并构建一个DataSet

  • 小结

    • 了解RDD、DataFrame、DataSet之间的转换关系

07:数据结构抽象:反射构建

  • 目标实现将RDD通过反射构建为DataFrame和DataSet

  • 路径

    • step1:功能

    • step2:场景

    • step3:实现

  • 实施

    • 功能

      • 将RDD中的泛型进行修改,将没有Schema类型,更改为有Schema类型,将每条数据从String类型变成一个JavaBean

    • 场景

      • 适合于Schema比较固定的场景

    • 实现

      • 数据内容:电影评分数据:用户id、电影id、评分、时间

        196 242 3   881250949
      • 代码开发

         //step1:读取数据
            val inputData: RDD[String] = spark.sparkContext.textFile("datas/ml-100k/u.data")
            //step2:转换数据
            /**
              * 方式一:通过反射:将RDD中的没有Schema的类型转换为有Schema类型【样例类】
              */
            val dataRdd: RDD[MovieRating] = inputData
                .map(line => {
                  val Array(userId,itemId,rating,timestamp) = line.trim.split("\\s+")
                  //封装为样例类对象
                  MovieRating(userId,itemId,rating.toDouble,timestamp.toLong)
                })
            //转换
            val df1: DataFrame = dataRdd.toDF()
            df1.show()
            val ds1: Dataset[MovieRating] = dataRdd.toDS()
            ds1.show()

  • 小结

    • 实现将RDD通过反射构建为DataFrame和DataSet

08:数据结构抽象:自定义Schema

  • 目标实现将RDD通过自定义Schema构建为DataFrame和DataSet

  • 路径

    • step1:功能

    • step2:场景

    • step3:实现

  • 实施

    • 功能:自定义Schema,与RDD合并构建DataFrame

    • 场景:适合于字段不固定的场景

    • 实现    

      /**
            * 方式二:自定义Schema,与RDD进行合并,构建DataSet
          */
          //构建Row类型的RDD
          val rowRdd: RDD[Row] = inputData
            .map(line => {
              val Array(userId,itemId,rating,timestamp) = line.trim.split("\\s+")
              //封装为样例类对象
              Row(userId,itemId,rating.toDouble,timestamp.toLong)
            })
          //自定义构建Schema
          val schema = new StructType(Array(
            StructField("userId",StringType,true),
            StructField("itemId",StringType,true),
            StructField("rating",DoubleType,true),
            StructField("timestamp",LongType,true)
          ))
          //构建DataFrame
          val df2: DataFrame = spark.createDataFrame(rowRdd,schema)
          //将DF转换为DS
          val ds2: Dataset[MovieRating] = df2.as[MovieRating]
          //将DS转换为DF
          ds2.toDF()//沿用ds中的列名
          ds2.toDF("user","moiveId","rate","time") //重新给每一列命名
      ​
          //step3:保存结果
          df2.show()
          ds2.show()
  • 小结

    • 实现将RDD通过自定义Schema构建为DataFrame和DataSet

09:DSL与SQL分析

  • 目标了解DSL与SQL分析的场景和性能

  • 实施

    • DSL开发

      • 利用函数式编程的方式来开发SparkSQL程序,类似于SparkCore方式

      • 流程

        • step1:读取数据放入DF或者DS

        • step2:调用函数来对DF或者DS进行转换

          • RDD的转换函数:map、flatMap、filter……

          • SparkSQL中的DSL函数:select、where、groupBy、limit、orderBy

        • step3:保存计算以后的结果

    • SQL开发

      • 利用SQL方式来开发SparkSQL程序,类似于Hive中的使用方式

      • 流程

        • step1:读取数据放入DF或者DS

        • step2:将DF或者DS注册为视图,调用SQL语句来实现处理

        • step3:保存计算以后的结果

    • 性能

      • DSL与SQL在性能上没有差异

        • DSL执行逻辑

           

        • SQL的执行逻辑

      • SparkSQL的底层还是SparkCore,所有计算最终还是由RDD的转换函数来实现

      • DS或者DF继承了RDD的特性

        • 所有转换都是Lazy模式

        • DS或者DF也继承了RDD的五大特性

        • 也拥有persist和checkpoint

  • 小结

    • 了解DSL与SQL分析的场景和性能

10:电影评分案例:数据与需求分析

  • 目标:了解电影评分案例的数据与需求分析

  • 路径

    • step1:数据内容

    • step2:需求

    • step3:分析

  • 实施

    • 数据内容

      UserID::MovieID::Rating::Timestamp
      1::1193::5::978300760
      1::661::3::978302109
      1::914::3::978301968
      1::3408::4::978300275
      1::2355::5::978824291
      1::1197::3::978302268
      1::1287::5::978302039
      1::2804::5::978300719
      • 四个字段

      • 分隔符:两个冒号

    • 需求

      • 基于电影评分数据:ratings.dat 统计平均评分最高的前10部电影【每部电影至少被评分2000次】

      • 要求

        • 使用DSL和SQL两种方式实现

        • 结果存储在文本和MySQL两种数据源中:数据源Sink

    • 分析

      • 结果

        电影id        平均评分        评分次数
      • SQL实现

        select
            movieId,
            avg(rating) as avgrate,
            count(movieId) as cnt
        from table
        group by movieId
        having cnt > 2000
        order by avgrate desc,cnt desc
        limit 10;
      • DSL实现

        ds
            .select($"movieId",$"rating")
            .groupBy("movieId")
            .agg(
                avg(rating).as( avgrate),
                count(movieId).as(cnt)
            )
            .where($"cnt" > 2000)
            .orderby($"avgrate".desc,$"cnt".desc)
            .limit(10)
  • 小结

    • 了解电影评分案例的数据与需求分析

11:电影评分案例:SQL实现

  • 目标实现SQL统计电影评分案例并保存结果至文本文件中

  • 实施

    package bigdata.spark.sql.movie
    ​
    import org.apache.spark.sql.{DataFrame, Dataset, SaveMode, SparkSession}
    ​
    /**
      * @ClassName SparkSQLMovieRating
      * @Description TODO SparkSQL实现电影评分案例开发
      */
    object MovieCaseSQL {
      def main(args: Array[String]): Unit = {
        //todo:1-构建驱动对象:SparkSession
        val spark: SparkSession = SparkSession
          .builder() //构造一个建造器
          .master("local[2]") //配置运行的模式
          .appName(this.getClass.getSimpleName.stripSuffix("$")) //设置程序名称
    //      .config("key","valule") //配置其他属性
          .getOrCreate() //返回一个SparkSession对象
        //更改日志级别
        spark.sparkContext.setLogLevel("WARN")
        //引入隐式转换
        import spark.implicits._
    ​
        //todo:2-实现处理的逻辑:统计平均评分最高的前10部电影【每部电影至少被评分2000次】
        //step1:读取数据
        val inputData: Dataset[String] = spark.read.textFile("datas/ml-1m/ratings.dat")
        //step2:转换数据
        val etlData: DataFrame = inputData
            .map(line => {
              val arr  = line.trim.split("::")
              (arr(0),arr(1),arr(2).toDouble,arr(3).toLong)
            })
            //所有字段重命名
            .toDF("userId","movieId","rating","time")
        //注册为视图
        etlData.createOrReplaceTempView("tmp_view_movie")
        //使用SQL进行处理
        val rsData = spark.sql(
          """
            |select
            |      movieId,
            |      round(avg(rating),2) as avgrate,
            |      count(movieId) as cnt
            |from tmp_view_movie
            |group by movieId
            |having cnt > 2000
            |order by avgrate desc,cnt desc
            |limit 10
          """.stripMargin)
        //step3:保存结果
        rsData.printSchema()
        rsData.show()
        //保存到文本:csv
        rsData
            .write //实现输出
            .mode(SaveMode.Overwrite) //如果输出已经存在,直接覆盖
            .csv("datas/sparksql/output1")
    ​
    ​
        //todo:3-释放资源
        spark.stop()
      }
    }
  • 小结

    • 实现SQL统计电影评分案例并保存结果至文本文件中

12:电影评分案例:DSL实现

  • 目标实现DSL统计电影评分案例并保存结果至MySQL中

  • 实施

    package bigdata.spark.sql.movie
    ​
    import java.util.Properties
    ​
    import org.apache.spark.sql.{DataFrame, Dataset, SaveMode, SparkSession}
    ​
    /**
      * @ClassName SparkSQLMovieRating
      * @Description TODO SparkSQL实现电影评分案例开发
      */
    object MovieCaseDSL {
      def main(args: Array[String]): Unit = {
        //todo:1-构建驱动对象:SparkSession
        val spark: SparkSession = SparkSession
          .builder() //构造一个建造器
          .master("local[2]") //配置运行的模式
          .appName(this.getClass.getSimpleName.stripSuffix("$")) //设置程序名称
    //      .config("key","valule") //配置其他属性
          .getOrCreate() //返回一个SparkSession对象
        //更改日志级别
        spark.sparkContext.setLogLevel("WARN")
        //引入隐式转换
        import spark.implicits._
        //引入DSL函数库
        import org.apache.spark.sql.functions._
    ​
        //todo:2-实现处理的逻辑:统计平均评分最高的前10部电影【每部电影至少被评分2000次】
        val inputData: Dataset[String] = spark.read.textFile("datas/ml-1m/ratings.dat")
        //step2:转换数据
        val rsData = inputData
          .map(line => {
            val arr  = line.trim.split("::")
            (arr(0),arr(1),arr(2).toDouble,arr(3).toLong)
          })
          //所有字段重命名
          .toDF("userId","movieId","rating","time")
          .select($"movieId",$"rating") //先过滤列
          .groupBy($"movieId")    //分组
          //聚合
          .agg(
            //平均分
            round(avg($"rating"),2).as("avgrate"),
            //评分次数
            count($"movieId").as("cnt")
          )
          .where($"cnt".gt(2000)) //过滤次数
          .orderBy($"avgrate".desc,$"cnt".desc) //排序
          .limit(10) //取前10
    ​
    ​
        //step3:保存结果
        rsData.printSchema()
        rsData.show()
        //保存到MySQL
        rsData
            .write
            .mode(SaveMode.Overwrite)
            .option("user","root")
            .option("password","123456")
            //jdbc(url: String, table: String, connectionProperties: Properties)
            .jdbc("jdbc:mysql://node1.itcast.cn:3306/?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true",
            "db_test.tb_top10_movies",new Properties())
    ​
    ​
        //todo:3-释放资源
        spark.stop()
      }
    }
    ​
  • 小结

    • 实现SQL统计电影评分案例并保存结果至文本文件中

13:SaveMode与Shuffle分区数

  • 目标掌握SaveMode及Shuffle分区数的使用

  • 实施

    • SaveMode:保存模式

      • Append:追加模式

      • ErrorIfExists:如果数据结果已存在就抛出异常

      • OverWrite:覆盖模式

      • Ignore:如果数据已存在,就忽略

    • Shuffle分区数

      • 问题:SparkSQL程序不论数据量的多少,在经过聚合shuffle时,RDD分区数会变为200

      • 原因

      • 解决

        • 根据数据量调整分区个数

          spark
          	.builder
          	.config("spark.sql.shuffle.partitions","2") //调整Shuffle分区个数为2

  • 小结

    • 掌握SaveMode及Shuffle分区数的使用

14:DataSet的设计

  • 目标了解SparkSQL中DataSet的设计

  • 实施

    • 设计

      • 目的:为了实现SparkSQL中所有数据的分布式存储

        • RDD:没有Schema

        • DataFrame:不支持泛型

        • DataSet:数据 + Schema + 支持泛型

      • 过程:所有数据读取到程序中,都变成DataSet

        • 如果数据中有Schema,直接使用数据中的Schema信息

        • 如果数据中没有Schema,DataSet会自动构建Schema,默认每一行只有一列:value

    • 优点

      • DataSet类型是一种强类型机制,在编译时可以发现语法和逻辑错误

      •  

    • 转换

      • RDD、DataFrame、DataSet之间的转换

    • 对比

      • DataSet的性能要比RDD的性能更好,DataSet拥有数据的Schema,可以根据代码逻辑对数据做更深的优化

        • 同样的数据内存,DataSet做了数据编码,所占的空间更小

        • DataSet包含了泛型和Schema,在编译时可以提高更多的优化程度

  • 小结

    • 了解SparkSQL中DataSet的设计

15:数据源Source:设计

  • 目标了解SparkSQL中Source数据源的设计

  • 实施

    • 设计:非常类似于MR的设计

      • MR的设计

        • InputFormat:输入类

          • TextInputFormat:读取文件

          • DBInputFormat:读取JDBC

          • TableInputFormat:读取Hbase

      • SparkSQL的设计

        • DataFrameReader:输入类

          • text/textFile:普通文本文件

          • json:JSON文件

          • csv:读取csv文件或者任何有固定分隔符的文件

          • parquet:Parquet文件

          • orc:Orc文件

          • jdbc:读取数据库

          • table:Hive表

    • 语法

      spark.read.json("文件路径")
          ||
      spark.read.format("json").load("文件路径")
  • 小结

    • 了解SparkSQL中Source数据源的设计

17:数据源Source:Parquet

  • 目标实现SparkSQL读取Parquet文件

  • 实施

        //todo:Parquet文件:SparkSQL默认读取的文本类型就是Parquet
        val p1 = spark.read.parquet("datas/resources/users.parquet")
        p1.show()
        val p2 = spark.read.format("parquet").load("datas/resources/users.parquet")
        p2.show()
        val p3 = spark.read.load("datas/resources/users.parquet")
        p3.show()
  • 小结

    • 实现SparkSQL读取Parquet文件

18:数据源Source:JSON

  • 目标实现SparkSQL读取JSON文件

  • 实施

     //todo:JSON文件
        val j1 = spark.read.json("datas/resources/people.json")
        val j2 = spark.read.format("json").load("datas/resources/people.json")
        j1.show()
        j2.show()
        val j3 = spark.read.textFile("datas/resources/people.json")
        j3
          //对于某个字段为JSON格式,可以使用函数进行解析
          .select(
            get_json_object($"value","$.age").as("age"),
            get_json_object($"value","$.name").as("name")
          )
          .show()
  • 小结

    • 实现SparkSQL读取JSON文件

19:数据源Source:CSV

  • 目标实现SparkSQL读取CSV文件

  • 实施

     //todo:CSV文件:以逗号分隔的文件,允许自定义分隔符
        val c1 = spark
          .read
          .csv("datas/sparksql/output1/part-00000-550b99bf-85be-467b-bf3b-f347aac19368-c000.csv")
    //    c1.show()
        //读取TSV格式的文件
        val t1 = spark
            .read
            .option("sep","\t")//指定文件的分隔符
            .option("header","true")//使用第一行作为列名
            .option("inferSchema","true")//自动推断类型
            .csv("datas/ml-100k/u.dat")
        t1.printSchema()
        t1.show()
  • 小结

    • 实现SparkSQL读取CSV文件

20:数据源Source:MySQL

  • 目标实现SparkSQL读取MySQL数据

  • 实施

    • 方式一

      def jdbc(
            url: String,							//JDBC的连接地址
            table: String,						//读取的表名称
            columnName: String,					//指定按照哪一列划分分区,这一列必须为数值类型	id【1 ~ 100】
            lowerBound: Long,						//分区字段取值的下界  5
            upperBound: Long,						//分区字段取值的上界  90
            numPartitions: Int,					//指定读取的数据放入几个分区
            connectionProperties: Properties		//配置属性
      )
      • 优点:可以在读取时就指定分区个数

      • 缺点:控制不是精准控制

      • 注意:columnName + lowerBound + upperBound ,不是用于过滤数据,只是决定数据怎么分区的

    • 方式二

       def jdbc(
       		url: String, 						//JDBC的url
       		table: String, 						//读取的表名
       		properties: Properties				//配置属性
       )				
      //读取MySQL数据
      val url = "jdbc:mysql://node1.itcast.cn:3306/?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true"
      val table = "db_test.tb_top10_movies"
      val prop = new Properties()
      prop.put("user","root")
      prop.put("password","123456")
      spark.read
      .jdbc(url,table,prop)
      .show()
      • 优点:简单,不用考虑并行度,如果数据量相对比较大,可以重分区

    • 缺点:如果数据量太大,会导致内存溢出

  • 小结

    • 实现SparkSQL读取MySQL数据

附录一: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>

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值