目录
DataFrame & DataSet & RDD 三者区别
spark.version = 2.4.4
站在上帝角度学习下SparkSQL架构相关内容
SparkSQL
SparkSQL 是一个用于处理结构化数据的Spark组件,结构化数据既可以来自外部结构化数据源,也可以通过向已有RDD增加Schema方式得到。
DataFrame API
通俗点理解也可以说SparkSQL主要完成SQL解析相关工作,将一个SQL语句解析为DataFrame或者RDD任务。
如下图可以看到实际上Spark的Dataframe API底层也是基于Spark的RDD。
DataFrame 是一种以RDD为基础的分布式数据集,类似于传统数据库中的二维表格。它也支持各种关系操作优化执行,与RDD不同的是,DataFrame带有Schema元数据,即DataFrame所表示的二维表数据集的每一列都带有名称和类型。由于无法知道RDD数据集内部数据结构类型,Spark作业执行只能在调度阶段层面进行简单通用的优化,而对于DataFrame带有数据集内部的结构,可以根据这些信息进行针对性的优化,最终实现优化运行效率。通俗的说SparkSQL能自动优化任务执行过程。
DataFrame & DataSet & RDD 三者区别
RDD 是一个不可变的分布式对象集合,是 Spark 对数据的核心抽象。每个 RDD 都被分为多个分区,每个分区就是一个数据集片段,这些分区运行在集群中的不同节点上。RDD 提供了一种高度受限的内存共享模型,即 RDD 是只读的,只能基于稳定的物理储存中的数据集来创建 RDD 或对已有的 RDD 进行转换操作来得到新的 RDD。
DataFrame 是用在 Spark SQL 中的一种存放 Row 对象的特殊 RDD,是 Spark SQL中的数据抽象,它是一种结构化的数据集,每一条数据都由几个命名字段组成(类似与传统数据库中的表),DataFrame 能够利用结构信息更加高效的存储数据。同时, SparkSQL 为 DataFrame 提供了非常好用的 API,而且还能注册成表使用 SQL 来操作。DataFrame 可以从外部数据源创建,也可以从查询结果或已有的 RDD 创建。
Dataset 是 Spark1.6 开始提供的 API,是 Spark SQL 最新的数据抽象。它把 RDD 的优势(强类型,可以使用 lambda 表达式函数)和 Spark SQL 的优化执行引擎结合到了一起。Dataset 可以从 JVM 对象创建得到,而且可以像 DataFrame 一样使用 API 或 sql 来操作。
三者的关系:RDD + Schema = DataFrame = Dataset[Row]
注:RDD 是 Spark 的核心,DataFrame/Dataset 是 Spark SQL 的核心,RDD 不支持 SQL 操作。
接下来通过简单示例&源码看下三者怎样转换的?
val df:DataFrame = spark.sql(sqlText)
df.printSchema()
val rdd = df.rdd.map(...)
import spark.implicits._
val a = rdd.toDF()
// 首先执行spark.sql()将返回一个DataFrame
def sql(sqlText: String): DataFrame = {
Dataset.ofRows(self, sessionState.sqlParser.parsePlan(sqlText))
}
// 将DataFrame转换为RDD
lazy val rdd: RDD[T] = {
val objectType = exprEnc.deserializer.dataType
rddQueryExecution.toRdd.mapPartitions { rows =>
rows.map(_.get(0, objectType).asInstanceOf[T])
}
}
// 将RDD转换为DataFrame
def toDF(): DataFrame = new Dataset[Row](sparkSession, queryExecution, RowEncoder(schema))
package object sql {
/**
* Converts a logical plan into zero or more SparkPlans. This API is exposed for experimenting
* with the query planner and is not designed to be stable across spark releases. Developers
* writing libraries should instead consider using the stable APIs provided in
* [[org.apache.spark.sql.sources]]
*/
@DeveloperApi
@InterfaceStability.Unstable
type Strategy = SparkStrategy
type DataFrame = Dataset[Row]
}
SparkSQL 组成
SparkSQL由Core、Catalyst、Hive和Hive-Thriftserver这四部分组成。
其中:
Catalyst:SparkSQL中的优化器,负责处理查询语句的整个处理过程,包括解析、绑定、优化、物理计划等;
Hive:兼容Hive,支持对Hive数据的处理;
Core:负责处理数据的输入/输出,从不同的数据源获取数据(JSON、RDD、Parquet、CSV)然后将查询结果输出为DataFrame;
Hive-ThriftServer:提供CLI和JDBC/ODBC等;
SparkSQL Catalyst Optimizer
SparkSQL基于Scala函数式编程结构设计了一个可扩展优化器,即Catalyst。这也是SparkSQL中较为核心的内容。
Catalyst支持基于规则和基于成本的优化。
在这个基础上,构建了专门用于关系查询处理的库和若干规则集来处理查询执行不同阶段:分析、逻辑优化、物理规划等。
其次Catalyst核心部分包含一个用于表示树和规则来操作它们。
接下来在讲解SparkSQL运行原理前将简单学习下Tree&Rule这两个重要概念,以及Catalyst大致流程是怎样的。
Tree
Catalyst中的主要数据类型是由节点对象组成的树,是Catalyst执行计划表示的数据结构。
TreeNode
新的节点类型在Scala中定义为TreeNode类的子类,
而Tree的具体操作是通过TreeNode来实现的,LogicalPlans,Expressions和Pysical Operators都可以使用Tree来表示。
Tree具备一些Scala Collection的操作能力和树遍历能力。
在SparkSQL中会根据语法生成一棵树,该树一直在内存里维护,不会保存到磁盘以某种格式的文件存在,且无论是Analyzer分析过的逻辑计划还是Optimizer优化过的逻辑计划,树的修改都是以替换已有节点的方式进行的。这些对象是不可变的,但可以使用函数转换进行操作。
每个节点都有一个节点类型和零个或多个子节点。Tree内部定义了一个children: Seq[BaseType]方法,可以返回一系列孩子节点。
abstract class TreeNode[BaseType <: TreeNode[BaseType]] extends Product {
// scalastyle:on
self: BaseType =>
val origin: Origin = CurrentOrigin.get
/**
* Retur