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  |
+---+------+
<think>好的,我现在需要详细解释Spark SQL的工作原理和实现机制。首先,根据用户提供的引用资料,Spark SQL的核心在于Catalyst优化器和Tungsten执行引擎。我需要结合这些信息,逐步梳理整个流程。 首先,用户提到的引用[2]详细描述了Spark SQL的执行流程,包括Catalyst优化器的四个阶段:Parser、Analyzer、Optimizer和Planner。然后代码生成和执行阶段。我需要确保每个阶段都解释清楚,并且引用正确的资料。 接下来,引用[1]提到Catalyst优化器的重要性,确保生成最优化的分布式程序。而引用[5]则介绍了Tungsten执行引擎的优化技术,比如代码生成和内存管理。需要将这两部分结合起来,说明Catalyst如何优化逻辑计划,Tungsten如何提升执行效率。 另外,用户提供的示例中提到了代码示例,比如创建SparkSession的代码来自引用[4]。在解释Spark SQL的入口点时,需要提到SparkSession替代了旧的SparkConf和Context,并引用相关例子。 用户的问题可能还涉及数据源集成,引用[3]提到Spark SQL支持多种数据源,默认使用parquet格式。这部分需要简要提及,以展示Spark SQL的通用性。 需要确保回答结构清晰,分步骤说明每个组件的作用,比如Catalyst优化器的各个阶段,Tungsten的优化技术,以及整个流程如何从SQL查询到执行。同时,要正确使用引用标记,如[^1]、[^2]等,标注在相关内容的末尾。 可能还需要检查是否有遗漏的关键点,比如代码生成阶段如何将逻辑计划转为物理计划,以及物理计划如何被Tungsten执行。同时要注意用户要求使用中文回答,数学公式的正确格式,以及生成相关问题。 最后,生成的相关问题需要围绕Spark SQL的优化、执行引擎、与其他组件集成等方面,确保问题能够帮助用户深入理解主题。</think>### Spark SQL 工作原理及实现机制详解 #### 一、核心组件与流程 Spark SQL 的核心目标是将**结构化查询**(如 SQLDataFrame/DataSet 程序)转换为高效的分布式 Spark 任务。其工作流程分为以下关键阶段: 1. **解析与逻辑计划生成** - **Parser 阶段**:将 SQL 语句或 DataFrame 操作解析为**未绑定的逻辑计划**(Unresolved Logical Plan)。例如,`SELECT * FROM table` 会被解析为抽象语法树(AST),但表名和列名尚未验证。 - **Analyzer 阶段**:通过元数据(如 Hive Metastore)绑定逻辑计划,验证表、列是否存在,并解析数据类型,生成**绑定的逻辑计划**(Resolved Logical Plan)[^2]。 2. **逻辑优化** - **Optimizer 阶段**:Catalyst 优化器应用规则(如谓词下推、常量折叠、列剪裁)优化逻辑计划。例如,将 `WHERE age > 10 AND age < 20` 合并为区间查询以减少数据读取量[^1]。 3. **物理计划生成** - **Planner 阶段**:将优化后的逻辑计划转换为**物理计划**(Physical Plan),选择最优执行策略(如 Broadcast Join 或 Sort Merge Join)。 4. **代码生成与执行** - **Code Generation**:Tungsten 引擎将物理计划编译为**Java 字节码**,避免解释执行开销。例如,将循环操作转换为紧凑的底层代码[^5]。 - **Execution**:通过 Spark Core 调度任务,分布式执行生成的代码,并返回结果[^1]。 #### 二、关键技术优化 1. **Catalyst 优化器** - **规则与成本优化**:内置数百条优化规则,支持自定义扩展。例如,通过 `ColumnPruning` 规则仅读取查询所需的列。 - **动态优化**:根据数据统计信息(如 Parquet 文件元数据)调整执行策略[^3]。 2. **Tungsten 执行引擎** - **内存管理**:使用堆外内存和二进制格式,减少 JVM 垃圾回收开销。 - **向量化处理**:按列批量处理数据,提升 CPU 缓存利用率。 - **代码生成示例**: ```python # 假设物理计划包含过滤操作 def filter_function(record): return record.age > 20 # Tungsten 生成类似的高效字节码替代解释执行 ``` 3. **统一入口与数据源集成** - **SparkSession**:取代传统 `SparkContext`,集成 SQL、流处理等功能入口[^4]。 - **多数据源支持**:通过 `DataFrameReader/Writer` 读写 Parquet、JSONHive 等格式,支持谓词下推和分区发现。 #### 三、性能对比示例 | 操作 | 未优化执行时间 | Catalyst 优化后时间 | Tungsten 优化后时间 | |---------------------|----------------|---------------------|---------------------| | 全表扫描 + 过滤 | 120s | 80s(列剪裁) | 50s(代码生成) | | 大表 Join 小表 | 300s | 200s(Broadcast) | 150s(向量化) | #### 四、应用场景 1. **交互式查询**:直接执行 SQL 分析 Hive 表。 2. **ETL 流水线**:通过 DataFrame API 转换异构数据源。 3. **机器学习预处理**:与 MLlib 集成,完成特征工程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值