SparkSQL -- 01 【概述,特点,编程模型,基本编程练习】

SparkSQL

一、SparkSQL的发展

1.1、概述

SparkSQL,顾名思义,就是Spark生态体系中的构建在SparkCore基础之上的一个基于SQL的计算模块。SparkSQL的前身叫Shark,最开始的底层代码优化,sql的解析,执行引擎等等完全基于HIve,Shark的执行速度要比Hive高出一个数量级,但是Hive的发展制约了Shark,所以在15年中旬的时候,Shark项目结束,重新独立出来一个项目,就是SparkSQL,不再依赖Hive,做了独立的发展,逐渐的形成两条互相独立的业务:Sparksql,和Hive-OnSpark。在SparkSQL发展的过程中,同时也吸收了Shark有些特点:基于内存的列存储,动态字节码优化技术

1.2、特点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.3、总结

SparkSQL就是Spark生态体系中用于处理结构化数据的一个模块。结构化数据是什么?存储在关系型数据库中的数据,就是结构化数据;半结构化数据是什么?类似xml、json等的格式的数据被称之为半结构化数据;非结构化数据是什么?音频、视频、图片等为非结构化数据。

换句话说,SparkSQL处理的就是二维表数据。

二、SparkSQL的编程模型(即代码风格:DSL和SQL)

2.1、编程模型简介

主要通过两种方式操作SparkSQL,一种就是SQL,另一种为DataFrame和Dataset。

- SQL

​ SQL不用多说,就和Hive操作一样,但是需要清楚一点的时候,SQL操作的是表,所以要想用SQL进行操作,就需要将SparkSQL对应的编程模型转化成为一张表才可以。

​ 同时支持,通用sql和hiveql。

- DataFrame和Dataset

​ DataFrame和Dataset是SparkSQL中的编程模型。DataFrame和Dataset我们都可以理解为是一张mysql中的二维表,表有什么?表头,表名,字段,字段类型。RDD其实说白了也是一张二维表,但是这张二维表相比较于DataFrame和Dataset却少了很多东西,比如表头,表名,字段,字段类型,只有数据。

​ Dataset是在spark1.6.2开始出现出现的api,DataFrame是1.3的时候出现的,早期的时候DataFrame叫SchemaRDD,SchemaRDD和SparkCore中的RDD相比较,就多了Schema,所谓约束信息,元数据信息。

​ 一般的,将RDD称之为Spark体系中的第一代编程模型;DataFrame比RDD多了一个Schema元数据信息,被称之为Spark体系中的第二代编程模型;Dataset吸收了RDD的优点(强类型推断和强大的函数式编程)和DataFrame中的优化(SQL优化引擎,内存列存储),成为Spark的最新一代的编程模型。

2.2、RDD、DataFrame、DataSet的对比

2.2.1、RDD

弹性分布式数据集。可分区,不可变(只读),内部元素可以并行计算。是Spark对数据进行的一种抽象,RDD就是一种数据结构,里面包含了数据和操作数据的方法(可以片面的将RDD理解为集合)
RDD的弹性:
1、可以自动切换内存和磁盘(当内存不够时,存入磁盘)
2、当某一个RDD的数据丢失时,可以通过血缘关系追溯到上一个RDD重新计算,不用从头计算
3、当某一个任务或阶段执行失败后,会自动进行重试,默认4次
4、RDD中的数据是分区的,分区的大小可以自由设置和细粒度调整
RDD的分布式:
RDD的数据可以存放在多个节点上
数据集:
就是一个存放数据的集合

相对于与DataFrame和Dataset,RDD是Spark最底层的抽象,目前是开发者用的最多的,但逐步会转向DataFrame和Dataset(当然,这是Spark的发展趋势)

2.2.2、DataFrame

DataFrame:理解了RDD,DataFrame就容易理解些,DataFrame的思想来源于Python的pandas库,RDD是一个数据集,DataFrame在RDD的基础上加了Schema(描述数据的信息,可以认为是元数据,DataFrame曾经就有个名字叫SchemaRDD),下面有图片很形象的表现出来了两者的差别:

假设RDD中的两行数据长这样:
在这里插入图片描述
那么DataFrame中的数据长这样:
在这里插入图片描述

从上面两个图可以看出,DataFrame比RDD多了一个表头信息(Schema),像一张表了,DataFrame还配套了新的操作数据的方法:DataFrame API,下面会说到。

有了DataFrame这个高一层的抽象后,我们处理数据更加简单了,甚至可以用SQL来处理数据了,对开发者来说,易用性有了很大的提升。不仅如此,通过DataFrame API或SQL处理数据,会自动经过Spark 优化器(Catalyst)的优化,即使你写的程序或SQL不高效,也可以运行的很快!

2.2.3、DataSet

相对于RDD,Dataset提供了强类型支持,也是在RDD的每行数据加了类型约束
如下图:

假设RDD中的两行数据长这样:
在这里插入图片描述

那么Dataset中的数据长这样:
在这里插入图片描述

或者长这样(每行数据是个Object):
在这里插入图片描述

使用Dataset API的程序,会经过Spark SQL的 优化器(Catalyst) 进行优化。

目前仅支持Scala、Java API,尚未提供Python的API,相比DataFrame,Dataset还提供了编译时类型检查,对于分布式程序来讲,提交一次作业太费劲了(要编译、打包、上传、运行),到提交到集群运行时才发现错误,很烦,这也是引入Dataset的一个重要原因。

2.2.4、RDD、DataFrame、DataSet的对比

1、RDD和DataFrame的比较
RDD描述的是数据结构,以及提供了操作方式,并且具有弹性特点,也可以理解为RDD是一张二维表
DataFrame以前叫SchemaRDD,从名字上就可以知道,比RDD多了一个Schema,而schema就是元数据
元数据有表名,表头,字段,字段类型
DataFrame易用性更好,底层可以自动优化。即使你写的sql比较复杂,运行速度也非常快
2、RDD和DataSet的比较
相同点:DataSet也引入了RDD的强类型推断,也是在RDD的每行数据加了类型约束
不同点:DataSet还可以映射成java对象
运行时,DataSet也会自动优化
3、DataFrame与DataSet的比较
相同点:都有schema
不同点:DataFrame 没有 编译时检查机制
DataSet 编译时检查机制

三、SparkSQL的编程入口

在SparkSQL中的编程模型,不在是SparkContext,但是创建需要依赖SparkContext。SparkSQL中的编程模型,在spark2.0以前的版本中为SQLContext和HiveContext,HiveContext是SQLContext的一个子类,提供Hive中特有的一些功能,比如row_number开窗函数等等,这是SQLContext所不具备的,在Spark2.0之后将这两个进行了合并——SparkSession。
SparkSession的构建需要依赖SparkConf或者SparkContext。
使用工厂构建器(Builder方式)模式创建SparkSession。

四、IDEA中编写SparkSQL的入口

4.1、在IDEA中创建SparkSQL模块

只要引入SparkSQL相关的依赖即可,如下:

<properties>
    <scala.version>2.11.8</scala.version>
    <spark.version>2.2.3</spark.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.scala-lang</groupId>
        <artifactId>scala-library</artifactId>
        <version>${scala.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-sql_2.11</artifactId>
        <version>${spark.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-hive_2.11</artifactId>
        <version>${spark.version}</version>
    </dependency>
</dependencies>

4.2、下面是创建SparkSQL入口的语法:SQLContext,HiveContext,SparkSession

import org.apache.spark.sql.hive.HiveContext
import org.apache.spark.sql.{DataFrame, SQLContext, SparkSession}
import org.apache.spark.{SparkConf, SparkContext}

/**
 * sparksql的程序入口方法
 */
object AppAccessDemo {
    def main(args: Array[String]): Unit = {
        //spark2.0以前,有两个,分别是sqlContext和hiveContext
        val conf: SparkConf = new SparkConf().setMaster("local").setAppName("test")
        val sc = new SparkContext(conf)

        //第一种:获取一个sqlContext
        val sqlContext = new SQLContext(sc)
        val df: DataFrame = sqlContext.read.json("data/a.json")
        df.show()

        //第二种:获取一个hiveContext
        val hiveContext = new HiveContext(sc)
        val frame: DataFrame = hiveContext.table("")
        frame.show()

        //第三种:使用spark2.0以后的SparkSession对象
        val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
        val df: DataFrame = spark.read.json("data/a.json")
        df.show()
        sc.stop()

    }
}

五、SparkSQL的基本编程

5.1、SparkSession的创建方式

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

object _02SparkSessionCreateMethod {
    def main(args: Array[String]): Unit = {
        //第一种:
        val spark: SparkSession =SparkSession.builder().appName("test").master("local").getOrCreate()
        val df: DataFrame = spark.read.json("data/a.json")
        df.show()

        //第二种
        val conf: SparkConf = new SparkConf().setAppName("test").setMaster("local")
        val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
        val df: DataFrame = spark.read.json("data/a.json")
        df.show()

        //第三种
        val spark: SparkSession = SparkSession.builder().appName("test")
            .master("local")
            .enableHiveSupport() //开启访问hive的支持
            .getOrCreate()

        spark.table("").show()
    }
}

5.2、基本编程

package com.xxx.SparkSQL.Day01

import org.apache.spark.sql.types.StructType
import org.apache.spark.sql.{Column, DataFrame, SparkSession}

object _03SparkSqlFirst {
    def main(args: Array[String]): Unit = {
        val spark: SparkSession = SparkSession.builder().appName("test").master("local").getOrCreate()

        //读取一个json文件,获得一个DataFrame对象,数据格式如下:
        // {"movie":"1193","rate":"5","datetime":"978300760","uid":"1"}
        val df: DataFrame = spark.read.json("data/a.json")

        //show():是一个行动算子,用于将结果打印到控制台,相当于sql的 select * from tableName
        //df.show()  //默认显示前20行
        //df.show(30,true)  指定要显示的行数,和字段的值的长度如果超出了30个字符是否要截断显示  就是一个
        //cell中的内容大于30个字符只显示30个字符,并且右对齐  ,false表示全部显示,左对齐
        df.show()

        //printSchema():此方法将显示二维表的结构(元数据:表明,表头,字段名,字段类型)
        df.printSchema()
        //也可以这样写,比较繁琐,不常用
        val schema: StructType = df.schema
        schema.printTreeString()

        //具体的一些查询
        df.select("movie","rate","uid").show()

        //导入SparkSession中的隐式转换操作,增强SQL的功能.注意:spark不是包名,而是sparkSeesion对象的名称
        import spark.implicits._
        //这时就可以使用变量了,变量可以用.的方式来调用方法
        df.select(($"movie").+(10),$"rate" +10,$"datetime").show()
        //还有一种字段的写法  使用new Column
        df.select(new Column("movie"),new Column("uid")).show()

        //起别名
        df.select($"movie" as("电影"),$"rate".as("评分"),$"datetime".as("时间")).show()

        //做聚合,统计
        df.select($"movie" as("电影")).groupBy("movie").count().show()

        //条件查询
        df.select($"movie" as("电影"),$"rate".as("评分"),$"datetime".as("时间"))
            .where("rate > 4 and datetime < 956354156")
            .show()


        /*
        上面的写法都是使用DataFrame或者DataSet,下面写SQL的写法
        要用SQL的写法必须事先将对应的数据映射为一张表才行,有四种映射方法
        df.createOrReplaceGlobalTempView():   global是全局的意思,表示整个spark程序中都可以访问到
        df.createGlobalTempView()                              没有global:仅当前任务中可以访问的
        df.createOrReplaceTempView()          replace:   有replace,表示如果存在就会替换,不存在也创建
        df.createTempView()                              无replace, 如果存在就报错,不存在就创建
        注意:建议使用  createOrReplaceTempView 或 createTempView
         */
        df.registerTempTable("movie") //在spark2.0之后就抛弃了换用下面的方法
        df.createOrReplaceTempView("movie")
        //使用SQL语法进行查询
        spark.sql(
            """
              |select
              |*
              |from movie
              |where rate > 4
              |""".stripMargin).show()
        //也可以提前定义SQL语句
        val sql = "select * from movie where rate > 4"
        spark.sql(sql).show()
        
        //关闭SparkSession(用完关闭,养成好习惯)
        spark.stop()
    }
}

5.3、SparkSQL编程模型的操作

5.3.1、DataFrame的构建

构建方式有三种:
1、就是上面使用的用read()方法读取文件,返回一个DataFrame
2、通过javaBean+反射来构建
3、通过动态编码的方式来构建

package com.xxx.SparkSQL.Day01

import org.apache.spark.rdd.RDD
import org.apache.spark.sql.types.{DataTypes, StructField, StructType}
import org.apache.spark.sql.{DataFrame, Row, SparkSession}

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

        javaBean
        dynamicProgramme
    }

    def javaBean: Unit = {
        val spark: SparkSession = SparkSession.builder().master("local").appName("create").getOrCreate()
        import spark.implicits._

        //描述一个java的学生集合对象,要使用java中对应的class
        val students = List(new Student(1001, "zs", "f", 23),
            new Student(1002, "ls", "m", 23),
            new Student(1003, "ww", "f", 25),
            new Student(1004, "zl", "f", 26))

        //调用createDataFrame方法需要导包
        import scala.collection.JavaConversions._
        //调用SparkSession的createDataFrame方法
        val df: DataFrame = spark.createDataFrame(students, classOf[Student])
        df.show()

        spark.stop()
    }

    def dynamicProgramme: Unit = {
        val spark: SparkSession = SparkSession.builder().master("local").appName("create").getOrCreate()
        import spark.implicits._

        //获取一个RDD对象,要使用SparkContext,SparkSession对象里可以直接获取
        //Row:行,就是代表了二维表中的一行记录
        val rdd1: RDD[Row] = spark.sparkContext.parallelize(List(
            Row(1001, "zs", "m", 23),
            Row(1002, "ls", "m", 24),
            Row(1003, "ww", "m", 25),
            Row(1004, "zl", "m", 26),
            Row(1005, "xq", "m", 27)
        ))

        //获取RDD的描述信息(RDD数据的元数据)
        val schema: StructType = StructType(List(
            StructField("id", DataTypes.IntegerType, false), //false代表不可为空,true可为空
            StructField("name", DataTypes.StringType, false),
            StructField("gender", DataTypes.StringType, false),
            StructField("age", DataTypes.IntegerType, false)
        ))

        val df: DataFrame = spark.createDataFrame(rdd1, schema)
        df.show()
        
        spark.stop()
    }
}

说明,这里学习三个新的类:

Row:代表的是二维表中的一行记录,或者就是一个Java对象
StructType:是该二维表的元数据信息,是StructField的集合
StructField:是该二维表中某一个字段/列的元数据信息(主要包括,列名,类型,是否可以为null)

总结:

这两种方式,都是非常常用,但是动态编程更加的灵活,因为javabean的方式的话,提前要确定好数据格式类型,后期无法做改动。

5.3.2、DataSet的构建

DataSet就是DataFrame的升级版,创建方式和DataFrame类似,但有所不同

package com.xxx.SparkSQL.Day01

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

/**
 * DataSet就是DataFrame的升级版
 *
 *  spark.createDataset(List|Seq|Array)
 * 创建方式是一样的,都可以使用动态编程方式
 * 1. 参数是scala的集合对象
 * 2. 有一个隐式参数:需要导入spark的隐式方法
 * 3. 集合的元素类型是一个样例类
 */
object _05CreateDataSetMethod {
    def main(args: Array[String]): Unit = {
        val spark: SparkSession = SparkSession.builder().master("local").appName("create").getOrCreate()
        import spark.implicits._

        //维护一个普通集合
        val girls = List(
            Girls(1001,"zs","m",23),
            Girls(1002,"ls","m",24),
            Girls(1003,"ww","m",25),
            Girls(1004,"zl","m",26)
        )

        val ds: Dataset[Girls] = spark.createDataset(girls)
        ds.show()

        spark.stop()
    }
}

case class Girls(id: Int, name: String, gender: String, age: Int)

在创建Dataset的时候,需要注意数据的格式,必须使用case class,或者基本数据类型,同时需要通过import spark.implicts._来完成数据类型的编码,而抽取出对应的元数据信息,否则编译无法通过

5.3.3、RDD和DataFrame以及DataSet之间的相互转换

1、RDD转DataFrame,DataSet
import spark.implicits._
rdd.toDF()
rdd.toDS()
2、DataFrame,DataSet转RDD
df.rdd
ds.rdd
3、DataFrame转DataSet

DataFrame无法直接转DataSet,但是可以调用算子实现转换(orderBy,groupBy等)

val ds: Dataset[Row] = df.orderBy($"deptno".desc)
4、DatSet转DataFrame
ds.toDF

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值