Spark系列文章目录
第一章 初识Spark
第二章 Spark-Core核心模型(一)
第二章 Spark-Core核心模型(二)
第三章 Spark-Core编程进阶(一)
第三章 Spark-Core编程进阶(二)
第四章 Spark-SQL基础(一)
第四章 Spark-SQL基础(二)
第五章 Spark-SQL进阶(一)
第五章 Spark-SQL进阶(二)
第五章 Spark-SQL进阶(三)
文章目录
- Spark系列文章目录
- 第四章 Spark-SQL基础(一)
- 1.认识Spark-SQL
- 2.Spark-SQL历程
- 2.1Hive
- 2.2 Shark
- 3.Spark-SQL优点
- 4.Spark-SQL架构
- 4.1基础架构
- 4.2执行流程
- 4.3Catalyst 执行优化器
- 4.4优化策略
- 5.分布式SQL引擎
- 5.1运行Spark SQL CLI
- 5.2运行Thrift JDBC/ODBC Server
- 6.编程流程
- 7.Spark-SQL案例
第四章 Spark-SQL基础(一)
1.认识Spark-SQL
Spark SQL是Spark中处理结构化数据的模块。提供了一种新的编程抽象DataFrame/Dataset,并且可以充当分布式SQL查询引擎。
- 集成:无缝地将SQL查询集成到Spark程序中。
- 统一数据访问:使用统一的方式连接到常见数据源。
- Hive兼容:通过配置可以直接兼容Hive,运行查询Hive数据。
- 标准的连接:通过JDBC、ODBC连接。Spark SQL包括具有行业标准JDBC和ODBC连接的服务器模式。
2.Spark-SQL历程
2.1Hive
Hive是基于Hadoop的一个数据仓库工具
- 可以将结构化的数据文件映射为一张数据库表;
- 可以提供简单的SQL查询功能;
- 可以将SQL语句转换为MapReduce任务并行运算;
Hive计算引擎依赖于MapReduce框架
- 随着时代的发展,对数据提取转化加载(ETL)需求越来越大;
- 因此开发一个更加高效的SQL-on-Hadoop工具更加的迫切;
2.2 Shark
Shark便是其中之一
- 修改了Hive中内存管理、物理计划、执行这三个模块,运行在Spark引擎上,使得SQL查询的速度得到10-100倍的提升。
随着Spark的发展
- Shark对于Hive的太多依赖
- 制约了Spark各个组件的相互集成
- 所以提出了SparkSQL项目
- 2014年宣布:停止开发Shark,至此Shark的发展画上了句号
- SparkSQL作为Spark生态的一员继续发展
- 不再受限于Hive
- 只是兼容Hive
Hive on Spark是Hive的发展计划
- 该计划将Spark作为Hive的底层引擎之一
- 也就是说,Hive将不再受限于一个引擎
- 可以采用Map-Reduce、Spark等引擎
Shark的出现,使得SQL-on-Hadoop的性能比Hive有了10-100倍的提高。
摆脱了Hive的限制,SparkSQL的性与Shark对比,也有很大的提升。
为什么SparkSQL的性能会得到怎么大的提升呢?
3.Spark-SQL优点
SparkSQL主要是在以下三点做了优化(主要是与Spark核心对比):
- 内存列存储(In-Memory Columnar Storage)
- 采用了内存列存储之后,减少了对内存的消耗,减少JVM的GC性能开销。
- 字节码生成技术(bytecode generation,即CG)
- Spark SQL在其catalyst模块的expressions中增加了codegen模块
- 对于SQL语句中的计算表达式
- 比如select num + num from t这种的sql,就可以使用动态字节码生成技术来优化其性能。
- Scala代码优化
- 使用Scala编写的代码,对可能造成较大性能开销的代码,Spark SQL底层会使用更加复杂的方式进行重写,来获取更好的性能。
- 比如Option样例类、for循环、map/filter/foreach等高阶函数,以及不可变对象,都改成了用null、while循环等来实现,并且重用可变的对象。
SparkSQL的表数据在内存中存储不是采用原生态的JVM对象存储方式,而是采用内存列存储,如下图所示。
4.Spark-SQL架构
4.1基础架构
SparkSQL语句由三部分组成
- Projection(a1,a2,a3)
- Data Source(tableA)
- Filter(condition)
分别对应SQL查询过程中的Result、Data Source、Operation。
- SQL语句按Result–>Data Source–>Operation的次序描述。
4.2执行流程
当执行SQL语句的顺序为:
- 对读入的SQL语句进行解析(Parse)
- 分辨出SQL语句中哪些词是关键词(如SELECT、FROM、WHERE);
- 哪些是表达式;
- 哪些是Projection;
- 哪些是Data Source等;
- 从而判断SQL语句是否规范;
- 将SQL语句和数据库的数据字典(列、表、视图等等)进行绑定(Bind)。
- 如果相关的Projection、Data Source等都是存在的话,就表示这个SQL语句是可以执行的。
- 一般的数据库会提供几个执行计划,这些计划一般都有运行统计数据,数据库会在这些计划中选择一个最优计划(Optimize)。
- 计划执行(Execute),按Operation–>Data Source–>Result的次序来进行。
- 在执行过程有时候甚至不需要读取物理表就可以返回结果。
- 比如重新运行刚运行过的SQL语句,可能直接从数据库的缓冲池中获取返回结果。
4.3Catalyst 执行优化器
Catalyst 是 Spark SQL 执行优化器的代号,所有 Spark SQL 语句最终都能通过它来解析、优化,最终生成可以执行的 Java 字节码。
Catalyst 最主要的数据结构是树,所有 SQL 语句都会用树结构来存储,树中的每个节点有一个类(class),以及 0 或多个子节点。Scala 中定义的新的节点类型都是 TreeNode 这个类的子类。
Catalyst 另外一个重要的概念是规则。基本上,所有优化都是基于规则的。可以用规则对树进行操作,树中的节点是只读的,所以树也是只读的。规则中定义的函数可能实现从一棵树转换成一颗新树。
整个 Catalyst 的执行过程可以分为以下 4 个阶段:
- 分析阶段,分析逻辑树,解决引用;
- 逻辑优化阶段;
- 物理计划阶段,Catalyst 会生成多个计划,并基于成本进行对比;
- 代码生成阶段;
4.4优化策略
- RBO(Rule-based optimization)基于规则的优化
- 优化思路主要是减少参与计算的数据量以及计算本身的代价。
- CBO 基于代价优化策略
- 它充分考虑了数据本身的特点(如大小、分布)以及操作算子的特点(中间结果集的分布及大小)及代价,从而更好的选择执行代价最小的物理执行计划,即 SparkPlan。
Spark 以 DAG 的方法执行上述 Physical Plan。
在执行 DAG 的过程中,Adaptive Execution 根据运行时信息动态调整执行计划从而提高执行效率。
5.分布式SQL引擎
5.1运行Spark SQL CLI
Spark SQL CLI是一种在本地模式下运行Hive metastore的便捷工具。
注意,Spark SQL CLI无法与Thrift JDBC服务器通信。
启动Spark SQL CLI
./bin/spark-sql
操作如下图所示:
5.2运行Thrift JDBC/ODBC Server
- 启动Thrift JDBC/ODBC服务
./sbin/start-thriftserver.sh
- 默认情况下,服务器监听 localhost:10000。可以通过任一环境变量重写该行为,即:
export HIVE_SERVER2_THRIFT_PORT=<listening-port>
export HIVE_SERVER2_THRIFT_BIND_HOST=<listening-host>
- 使用beeline测试Thrift JDBC/ODBC服务器:
./bin/beeline
- 连接到JDBC/ODBC服务器:
beeline> !connect jdbc:hive2://localhost:10000
- beeline会询问你的用户名和密码。在非安全模式下,只需在机器上输入用户名和空白密码即可。
- 注意:如果遇到SQL查询结果中文显示乱码的问题,可以通过修改系统编码的方式解决,比如数据库是UTF-8编码,在启动beeline之前:
LANG=zh_CN.UTF-8;./bin/beeline
-
!exit
退出beeline终端
启动Thrift JDBC/ODBC服务,操作如下图所示:
使用beeline测试Thrift JDBC/ODBC服务器,操作如下图所示:
注意,用户名是在hive-site.xml中配置的,密码为任意即可。
6.编程流程
SparkSQL编程流程如下图所示:
7.Spark-SQL案例
添加依赖:
<!--Spark SQL-->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_${spark.scala.version}</artifactId>
<version>${spark.version}</version>
</dependency>
编码如下:
package com.briup.sql
import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
case class User(name:String,age:Int)
object SparkSqlFirst {
def main(args: Array[String]): Unit = {
//1.获取SparkSession对象
val spark=SparkSession.builder()
.master("local[*]")
.appName("spark_sql")
.getOrCreate();
//2.导入隐式支持
//会隐式将RDD对象转化为DataFarame/Dataset对象
import spark.implicits._;
//3.读取json数据源获取Dataset对象
val rowDS: Dataset[Row] =spark.read.json("spark/src/data/resources/people.json");
//将Row对象转化为User对象
val userDS: Dataset[User] =dataset.as[User];
//4.输出到控制台
userDS.show()
rowDS.show();
//5.关闭会话对象
spark.close();
}
}