Spark - SQL
历史
Hive
- 一开始,Hadoop/MapReduce 在企业生产中大量使用,在 HDFS 上积累了大量数据
- MapReduce 对于开发者而言使用难度较大,大部分开发人员最熟悉的还是传统的关系型数据库
- 为了方便大多数开发人员使用 Hadoop,诞生了 Hive
- Hive 提供类似 SQL 的编程接口,HQL 经过语法解析、逻辑计划、物理计划转化成 MapReduce 程序执行
- 使得开发人员很容易对 HDFS 上存储的数据进行查询和分析
Shark
- 在 Spark 刚问世时,Spark 团队开发了 Shark 来支持用 SQL 来查询 Spark 的数据
- Shark 的本质是 Hive,Shark 修改了 Hive 的内存管理模块,大幅优化了运行速度
- Shark 依赖于 Hive,严重影响了 Spark 的发展,Spark 要定义一个统一的技术栈和完整的生态
- 依赖于 Hive 还会制约 Spark 各个组件的相互集成,Spark 无法利用 Spark 的特性进行深度优化
- 2014 年 7 月 1 日,Spark 团队将 Shark 交给 Hive 进行管理,即 Hive on Spark,转而开发 Spark SQL
Spark SQL
- Spark SQL 摒弃了 Shark 的执行引擎,换成重新开发的执行引擎
- Spark SQL 不仅将关系型数据库的处理模式和 Spark 的函数式编程相结合
- 还兼容多种数据格式 - Hive、RDD、JSON 文件、CSV 文件等
- Spark SQL 大大加快了 Spark 生态的发展
架构
- Spark SQL 本质上是一个 Library,运行在 Spark 的核心执行引擎之上
- Spark SQL 提供类似于 SQL 的操作接口
- 允许数据仓库应用直接获取数据
- 允许使用者通过命令行操作来交互地查询数据
- 还提供两个 API - DataFrame API + DataSet API
- Java、Python 和 Scala 应用程序可以通过这两个 API 来读取和写入 RDD
- 应用程序还可以直接操作 RDD
- 使用 Spark SQL,开发者会觉得好像在操作一个关系型数据库一样,而不是在操作 RDD
- 与基本的 Spark RDD API 不同,Spark SQL 的接口提供了关于数据结构和正在执行的计算的更多信息
- 在内部,Spark SQL 使用这些额外的信息来执行额外的优化
- Spark SQL 支持多种交互方式,但在计算结果时均使用相同的执行引擎,可以在不同 API 之间来回切换
DataSet
- DataSet 即数据集,是 Spark 1.6 新引入的接口
- 与 RDD 类似,DataSet 也是不可变分布式的数据单元
- 既有与 RDD 类似的各类转换和动作函数的定义
- 还享受 Spark SQL 优化过的执行引擎,使得数据搜索效率更高
- DataSet 支持的转换和动作与 RDD 类似 - map、filter、count 等
- 与 RDD 类似,DataSet 上的转换操作不会被立即执行
- 先生成新的 DataSet,只有遇到动作操作,才会把之前的转换操作一并执行,生成结果
- DataSet 的内部结构包含了逻辑计划,即生成该数据集所需要的运算
- 当动作操作执行时,Spark SQL 的查询优化器会优化该逻辑计划
- 并生成一个可以分布式执行、包含分区信息的物理计划
DataSet vs RDD
- DataSet API 是 Spark SQL 的一个组件,DataSet 具有关系型数据库表中的特性
- DataSet 所描述的数据都被组织到有名字的列中,就像关系型数据库中的表一样
- RDD 以 People 为类型参数,但 Spark 框架本身并不了解 People 类的内部结构,所有操作都以 People 为单位
- DataSet 提供详细的结构信息和每列的数据类型
- DataSet 为 Spark SQL 提供了数据表的 Schema 信息,使得 DataSet API 的执行效率更高
- 由于 DataSet 存储了每列的数据类型,在程序编译时可以执行类型检测
DataFrame
- DataFrame 是一种特殊的 DataSet
- DataFrame 也是关系型数据库中表一样的结构化存储机制,也是分布式不可变的数据结构
- 但 DataFrame 的每一列并不存储类型信息,所以在编译过程并不能发现类型错误
- DataFrame 每一行的类型固定为 Row,可以被当作 DataSet[Row] 来处理
- 必须要通过解析才能获取到各列的值 - 类似于某些 JSON 库的处理
- DataSet - people.name
- DataFrame -
people.get As [String]("name")
RDD / DataFrame / DataSet
RDD | DataFrame | DataSet | |
---|---|---|---|
不可变性 | Yes | Yes | Yes |
分区 | Yes | Yes | Yes |
Schema | No | Yes | Yes |
查询优化器 | No | Yes | Yes |
API 级别 | 低 | 高(底层基于 RDD 实现) | 高(DataFrame 的拓展) |
是否存储类型 | Yes | No | Yes |
何时检测语法错误 | 编译时 | 编译时 | 编译时 |
何时检测分析错误 | 编译时 | 运行时 | 编译时 |
历史
- RDD API 在 Spark 1.x 中就已经存在,是整个 Spark 框架的基石
- 为了方便熟悉关系型数据和 SQL 的开发人员使用,在 RDD 的基础上,Spark 创建了 DataFrame API
- 从 Spark 1.6 开始,引入了 DataSet,在 DataFrame 的基础上,新增了对数据的每一列的数据类型的限制
- 在 Spark 2.0 中,DataFrame 和 DataSet 被统一,DataFrame 作为 DataSet[Row] 存在
- 在弱类型的语言中,如 Python 中的 DataFrame API 依然存在
- 在强类型的语言中,如 Java 中的 DataFrame API 已经不复存在
不可变性 + 分区
- DataFrame 和 DataSet 都是基于 RDD,都拥有 RDD 的基本特性
- 可以通过简单的 API 在 DataFrame 或 DataSet 与 RDD 之间进行无缝切换
性能
- DataFrame 和 DataSet 的性能要比 RDD 更好
- Spark 程序运行时,Spark SQL 中的查询优化器会对语句进行分析,并生成优化过的 RDD 在底层执行
错误检测
- RDD 和 DataSet 都是类型安全的,而 DataFrame 并不是类型安全的(并不存储每一列的信息)
- 使用 DataFrame API 时,可以选择一个并不存在的列,该错误只有在代码被执行时才会抛出
- 使用 DataSet API,在编译时就能检测到这个错误
小结
- DataFrame 和 DataSet 是 Spark SQL 提供的基于 RDD 的结构化数据抽象
- 既有 RDD 不可变、分区、存储依赖关系等特性
- 又拥有类似于关系型数据库的结构化信息
- 基于 DataFrame 和 DataSet API 开发的程序会被自动优化
- 无需开发人员操作底层的 RDD API 来进行手动优化,大大提升开发效率
- RDD API 对于非结构化的数据处理有独特的优势,并且更方便做底层操作
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.