Spark SQL详解

spark 系列

Spark 核心原理及运行架构

Spark RDD详解

Spark 常用算子大全

Spark SQL 详解



前言

看了前面的几篇 Spark 博客,相信大家对于 Spark 的基础框架、RDD和核心 SparkCore 已经搞明白了。本篇博客将为大家详细介绍了 Spark 一个重要的内置模块 Spark SQL 。这是工作中最常用的一个内置模块之一。


Spark SQL 简介

什么是Spark SQL?

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

我们已经学习了Hive,它是将Hive SQL转换成MapReduce然后提交到集群上执行,大大简化了编写MapReduce的程序的复杂性,由于MapReduce这种计算模型执行效率比较慢。所以Spark SQL的应运而生,它是将Spark SQL转换成RDD,然后提交到集群执行,执行效率非常快!同时Spark SQL也支持从Hive中读取数据。

Spark SQL 的由来

SQL是一种传统的用来进行数据分析的标准,为了解决SQL on hadoop的问题,早期有以下几种方案:

  • Hive是原始的SQL-on-Hadoop解决方案
  • Impala:和Hive一样,提供了一种可以针对已有Hadoop数据编写SQL查询的方法
  • Presto:类似于Impala,未被主要供应商支持
  • Shark:Spark SQL的前身,设计目标是作为Hive的一个补充
  • Phoenix:基于HBase的开源SQL查询引擎

Shark的初衷:让Hive运行在Spark之上。Spark 是对Hive的改造,继承了大量Hive代码,给优化和维护带来了大量的麻烦。

在这里插入图片描述

Spark SQL 的特点

1. 易整合(集成)
在这里插入图片描述

2. 统一的数据访问方式
在这里插入图片描述

3. 兼容Hive
在这里插入图片描述

4. 标准的数据连接
在这里插入图片描述

Spark SQL 框架结构

在这里插入图片描述
如上图所示,Spark SQL有以下主要功能:

  • Spark SQL是Spark的核心组件之一(2014.4 Spark1.0)
  • 能够直接访问现存的Hive数据
  • 提供JDBC/ODBC接口供第三方工具借助Spark进行数据处理
  • 提供了更高层级的接口方便地处理数据
  • 支持多种操作方式:SQL、API编程
  • 支持多种外部数据源:Parquet、JSON、RDBMS等

Spark SQL的核心 Catalyst优化器(了解)

在这里插入图片描述
Catalyst优化器是Spark SQL的核心

深入研究Spark SQL的Catalyst优化器(原创翻译) 对Spark SQL的核心 Catalyst优化器的原理写的很清楚,这里我就简单的说一下 Catalyst优化器 优化一条SQL语句的大致流程。

例如,如下一条简单的SQL语句,Catalyst优化器是如何去优化的呢?

SELECT name FROM
(
    SELECT id, name FROM people
) p
WHERE p.id = 1

如下图所示,Catalyst优化器的优化主要在投影上面查询过滤器并检查过滤是否可下压。一句话概括来说,Catalyst优化器的优化会自动选择最优执行效率的方案,这在过滤删选操作是非常实用的,可以减少hive SQL所必要的子查询嵌套,使代码变得简单一点。
在这里插入图片描述

Spark SQL 常用API

SparkContext 与 SparkSession

Spark 2.0以前的版本中,SparkSQL提供两种SQL查询起始点:一个叫SQLContext,用于Spark自己提供的SQL查询编程入口;一个叫HiveContext,是SQLContext的子集,提供更多的功能,主要用于连接Hive的查询。

SparkSession是Spark最新的SQL查询起始点,实质上是SQLContext和HiveContext的组合,所以在SQLContext和HiveContext上可用的API在SparkSession上同样是可以使用的。SparkSession内部封装了sparkContext,所以计算实际上是由sparkContext完成的。SparkSession提供与Spark功能交互单一入口点,并允许使用DataFrame和Dataset API对Spark进行编程。

SparkSession 创建语法

val spark = SparkSession.builder.master("master").appName("appName").getOrCreate()

注意:

  • 无特殊说明时,下文中“spark”均指SparkSession实例;
  • 如果是spark-shell下,会自动创建“sc”和“spark”;
  • master通常为local[*],制定开启CPU核数进行计算,*可为任何整数,如果为*本身,代表动用计算机现有可用CPU核数。

在spark的早期版本中,SparkContext是spark的主要切入点,由于RDD是主要的API,我们通过sparkcontext来创建和操作RDD。对于每个其他的API,我们需要使用不同的context。例如,对于Streming,我们需要使用StreamingContext;对于sql,使用sqlContext;对于Hive,使用hiveContext。但是随着DataSet和DataFrame的API逐渐成为标准的API,就需要为他们建立接入点。

SparkContext 创建语法:

val conf = new SparkConf().setMaster("master").setAppName("appName")
val sc = new SparkContext(conf)

创建sparkContext对象:主要用于读取需要处理的数据,封装在RDD集合中;调度jobs执行。

SparkSession内部封装了SparkContext,所以计算实际上是由SparkContext完成的。

DataFrame

与RDD类似,DataFrame也是一个分布式数据容器。然而DataFrame更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即schema。同时,与Hive类似,DataFrame也支持嵌套数据类型(struct、array和map)。从API易用性的角度上看,DataFrame API提供的是一套高层的关系操作,比函数式的RDD API要更加友好,门槛更低。
在这里插入图片描述
上图直观地体现了DataFrame和RDD的区别。左侧的RDD[Person]虽然以Person为类型参数,但Spark框架本身不了解Person类的内部结构。而右侧的DataFrame却提供了详细的结构信息,使得Spark SQL可以清楚地知道该数据集中包含哪些列,每列的名称和类型各是什么。DataFrame是为数据提供了Schema的视图。可以把它当做数据库中的一张表来对待,DataFrame也是懒执行的。性能上比RDD要高。

RDD是分布式的 Java对象的集合。DataFrame是分布式的Row对象的集合。DataFrame除了提供了比RDD更丰富的算子以外,更重要的特点是提升执行效率、减少数据读取以及执行计划的优化。

创建方式与使用

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

1. 通过Spark的数据源进行创建

json.txt 源数据:

{
   
   "name":"hubert","age":"100"}
{
   
   "name":"rose","age":"10"}
{
   
   "name":"jack","age":"39"}
{
   
   "name":"henry","age":"34"}
val spark = SparkSession.builder().appName("test").master("local[*]").getOrCreate()
// 读取json文件
val df = spark.read.format("json").load("data/json.txt")
// 操作
df.select("name","age").show(false)
spark.close()

// 结果
+------+---+
|name  |age|
+------+---+
|hubert|100|
|rose  |10 |
|jack  |39 |
|henry |34 |
+------+---+

2. 从RDD中转换

下面 RDD转换为DateFrame 会细说。

3. 从Hive Table进行查询返回

这个将在后面的博文中涉及到,这里暂且不谈。

SQL风格语法(主要)

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

    // 1)创建一个DataFrame
    val df = spark.read.json("data/json.txt")

    // 2)对DataFrame创建一个临时表
    df.createOrReplaceTempView("people")

    // 3)通过SQL语句实现查询全表
    val sqlDF = spark.sql("SELECT * FROM people")

    // 4)结果展示
    sqlDF.show(false)

    // 5)对于DataFrame创建一个全局表
    df.createGlobalTempView("people")

    // 6)通过SQL语句实现查询全表
    spark.sql("select * from global_temp.people").show(false)

    spark.close()
  }
}

注意:临时表是Session范围的,Session退出后,表就失效了。如果想应用范围内仍有效,可以使用全局表。注意使用全局表时需要全路径访问,如:global_temp:people。

全局的临时视图存在于系统数据库 global_temp中,我们必须加上库名去引用它

DSL 风格语法 (次要)

object Test04 {
   
   
  def main(args: Array[String]): Unit = {
   
   
    val spark = SparkSession.builder().appName("test").master("local[*]").getOrCreate()
    // 导入 sparkSession 对象spark的隐式类
    import spark.implicits._

    // 1)创建一个DataFrame
    val df = spark.read.json("data/json.txt")

    // 2)查看DataFrame的Schema信息
    df.printSchema()

    // 3)只查看"name"列数据
    df.select("name").show(false)

    // 4)查看"name"列数据以及"age+1"数据
    df.select($"name",$"age" + 1).show(false)

    // 5)查看"age"大于"21"的数据
    df.filter($"age" >21).show(false)

    // 6)按照"age"分组,查看数据条数
    df.groupBy("age").count().show()

    spark.close()
  }
}

结果:

root
 |-- age: string (nullable = true)
 |-- name: string (nullable = true)

+------+
|name  |
+------+
|hubert|
|rose  |
|jack  |
|henry |
+------+

+------+---------+
|name  |(age + 1)|
+------+---------+
|hubert|101.0    |
|rose  |11.0     |
|jack  |40.0     |
|henry |35.0     |
+------+---------+

+---+------+
|age|name  |
+---+------+
|100|hubert|
|39 |jack  |
|34 |henry |
+---+------+

+---<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值