大数据系统-SparkSQL基于内存的大数据分析引擎

[1]参考文章:高彦杰,陈冠诚 Spark SQL : 基于内存的大数据分析引擎《程序员》2014 . 8

AMPLab将大数据分析负载分为三大类型:批量数据处理、交互式查询、实时流处理。而其中很重要的一环便是交互式查询。大数据分析栈中需要满足用户ad-hoc、reporting、iterative等类型的查询需求,也需要提供SQL接口来兼容原有数据库用户的使用习惯,同时也需要SQL能够进行关系模式的重组。完成这些重要的SQL任务的便是 Spark SQL和Shark这两个开源分布式大数据查询引擎,它们可以理解为轻量级Hive SQL在Spark上的实现,业界将该类技术统称为SQL on Hadoop。

在Spark 峰会2014上,Databricks宣布不再支持Shark的开发,全力以赴开发Shark的下一代技术Spark SQL,同时Hive社区也启动了Hive onSpark项目,将Spark作为Hive(除MapReduce和Tez之外的)新执行引擎。根据伯克利的BigData Benchmark测试对比数据,Shark的In Memory 性能可以达到Hive的100倍,即使是On Disk 也能达到10倍的性能提升,是Hive的强有力的替代解决方案。而作为Shark的进化版本的Spark SQL,在AMPLab最新的测试中的性能已经超过Shark。图3-1展示了Spark SQL和Hive on Spark是新的发展方向。

Development……to Spark SQL:Shark开发终止,转向Spark SQL

A new……for Spark:基于Spark的新的SQL查询引擎

Help……to Spark:帮助现有Hive用户迁移到Spark

大数据系统-SparkSQL基于内存的大数据分析引擎_第1张图片

图3-1  Spark SQL和Hive on Spark是新的发展方向

.1.1为什么使用Spark SQL

由于Shark底层依赖于Hive,这个架构的优势是对传统Hive用户可以将Shark无缝集成进现有系统运行查询负载。但是我们也看到一些问题:随着版本升级,查询优化器依赖于Hive,不方便添加新的优化策略,需要进行另一套系统的学习和二次开发,学习成本很高。另一方面,MapReduce是进程级并行,例如:Hive在不同的进程空间会使用一些静态变量,当在同一进程空间进行多线程并行执行,多线程同时写同名称的静态变量会产生一致性问题,所以Shark需要使用另外一套独立维护的Hive源码分支。而为了解决这个问题AMPLab和Databricks利用Catalyst开发了SparkSQL。

Spark的全栈解决方案为用户提供了多样的数据分析框架,机器学习、图计算、流计算如火如荼的发展和流行吸引了大批的学习者,为什么我们今天还是要重视的在大数据环境下使用SQL呢?笔者认为主要有以下几点原因:

1)易用性与用户惯性。在过去的很多年中,有大批的程序员的工作是围绕着数据库+应用的架构来做的,因为SQL的易用性提升了应用的开发效率。程序员已经习惯了业务逻辑代码调用SQL的模式去写程序,惯性的力量是强大的,如果还能用原有的方式解决现有的大数据问题,何乐而不为呢?提供SQL和JDBC的支持会让传统用户像以前一样的书写程序,大大减少迁移成本。

2)生态系统的力量。很多系统软件性能好,但是未取得成功和没落,很大程度上因为生态系统问题。传统的SQL在JDBC、ODBC、SQL的各种标准下形成了一整套成熟的生态系统,很多应用组件和工具可以迁移使用,像一些可视化的工具、数据分析工具等,原有企业的IT工具可以无缝过渡。

3)数据解耦,Spark SQL正在扩展支持多种持久化层,用户可以使用原有的持久化层存储数据,但是也可以体验和迁移到Spark SQL提供的数据分析环境下进行Big Data的分析。

2Spark SQL架构分析

Spark SQL与传统的DBMS的查询优化器+执行器的架构较为类似,只不过其执行器是在分布式环境中实现,并采用的Spark作为执行引擎。SparkSQL的查询优化是Catalyst,其基于Scala语言开发,可以灵活利用Scala原生的语言特性很方便进行功能扩展,奠定了Spark SQL的发展空间。Catalyst将SQL语言翻译成最终的执行计划,并在这个过程中进行查询优化。这里和传统不太一样的地方就在于,SQL经过查询优化器最终转换为可执行的查询计划是一个查询树,传统DB就可以执行这个查询计划了。而Spark SQL最后执行还是会在Spark内将这棵执行计划树转换为Spark的有向无环图DAG再进行执行。

1.Catalyst架构及执行流程分析

下面我们可以看到Catalyst的整体架构:

大数据系统-SparkSQL基于内存的大数据分析引擎_第2张图片

Phases……relational queries:规则分析,优化,查询计划的各个阶段

Unresolved Logical Plan :未解析的逻辑查询计划

Logical Plan:逻辑查询计划

Optimized Logical Plan:优化的逻辑查询计划

Physical Plans:物理查询计划

Analysis Rules:分析规则

Optimization Rules:优化规则

Planning Rules:计划规则

图3-2  Spark SQL查询引擎Catalyst的架构

我们从图3-2中可以看到整个的Catalyst是Spark SQL的调度核心,遵循传统数据库的查询解析步骤,对SQL进行解析,转换为逻辑查询计划,物理查询计划,最终转换为Spark的DAG进行执行。图3-3为Catalyst的执行流程。

大数据系统-SparkSQL基于内存的大数据分析引擎_第3张图片

图3-3  Catalyst的执行流程

SQlParser将SQL语句转换为逻辑查询计划,Analyzer对逻辑查询计划进行属性和关系关联检验,之后Optimizer通过逻辑查询优化将逻辑查询计划转换为优化的逻辑查询计划,QueryPlanner将优化的逻辑查询计划转换为物理查询计划,prepareForExecution调整数据分布,最后将物理查询计划转换为执行计划进入Spark执行任务。

2.Spark SQL优化策略

查询优化是传统数据库中最为重要的一环,这项技术在传统数据库中已经很成熟。除了查询优化,Spark SQL在存储上也是进行了优化,下面我们从以下几点看看Spark SQL的一些优化策略:

(1)内存列式存储与内存缓存表

Spark SQL可以通过cacheTable将数据存储转换为列式存储,同时将数据加载到内存进行缓存。cacheTable相当于在分布式集群的内存物化视图,将数据进行缓存,这样迭代的或者交互式的查询不用再从HDFS读数据,直接从内存读取数据大大减少了I/O开销。列式存储的优势在于Spark SQL只需要读出用户需要的列,而不需要像行存储那样需要每次将所有列读出,从而大大减少内存缓存数据量,更高效的利用内存数据缓存,同时减少网络传输和IO开销。数据按照列式存储,由于是数据类型相同的数据连续存储,能够利用序列化和压缩减少内存空间的占用。

(2)列存储压缩

为了减少内存和硬盘空间占用,Spark SQL采用了一些压缩策略对内存列存储数据进行压缩。Spark SQL的压缩方式要比Shark丰富很多,例如它支持PassThrough,RunLengthEncoding, DictionaryEncoding, BooleanBitSet, IntDelta, LongDelta等多种压缩方式。这样能够大幅度减少内存空间占用和网络传输开销和I/O开销。

(3)逻辑查询优化

SparkSQL在逻辑查询优化(见图3-4)上支持列剪枝、谓词下压、属性合并等逻辑查询优化方法。列剪枝为了减少读取不必要的属性列,减少数据传输和计算开销,在查询优化器进行转换的过程中会进行列剪枝的优化。

下面我们介绍一个逻辑优化例子:

SELECT ClassFROM (SELECT ID,Name,Class  FROM STUDENT) S WHERE S.ID=1

 大数据系统-SparkSQL基于内存的大数据分析引擎_第4张图片

图3-4  逻辑查询优化

Catalyst将原有查询通过谓词下压,将选择操作ID=1优先执行,这样过滤大部分数据,通过属性合并将最后的投影只做一次最终保留Class属性列。

(4)Join优化

Spark SQL深度借鉴传统数据库的查询优化技术的精髓,同时也在分布式环境下进行特定的优化策略调整和创新。现在Spark SQL对Join进行了优化支持多种连接算法,现在的连接算法已经比Shark丰富,而且很多原来Shark的元素也逐步迁移过来。例如:BroadcastHashJoin、BroadcastNestedLoopJoin、HashJoin、LeftSemiJoin,等等。

下面我们介绍一个其中的BroadcastHashJoin算法思想:BroadcastHashJoin将小表转化为广播变量进行广播,这样避免Shuffle开销,最后在分区内做Hash连接。这里用的就是Hive中Map Side Join的思想。同时用了DBMS中的Hash连接算法做连接。

随着Spark SQL发展,未来会有更多的查询优化策略加入进来。同时后续Spark SQL会支持像Shark Server一样的服务端,JDBC接口,兼容更多的持久化层例如NoSQL,传统的DBMS等。一个强有力的结构化大数据查询引擎正在崛起。

3.如何使用Spark SQL

val sqlContext = new org.apache.spark.sql.SQLContext(sc)

// 在这里引入sqlContext下所有的方法我们就可以直接用sql方法进行查询。

import sqlContext._

case class Person(name: String, age: Int)

// 下面的people是含有case类型数据的RDD,会默认由Scala的implicit机制将RDD转换为SchemaRDD,SchemaRDD是SparkSQL中的核心RDD。

val people =sc.textFile("examples/src/main/resources/people.txt").map(_.split(",")).map(p=> Person(p(0), p(1).trim.toInt))

// 在内存的元数据中注册表信息,这样一个Spark SQL表就创建完成了。

people.registerAsTable("people")

// sql语句就会触发上面分析的SparkSQL的执行过程,读者可以参考上面的图示。

val teenagers =sql("SELECT name FROM people WHERE age >= 13 AND age <= 19")

// 最后生成teenagers也是一个RDD

teenagers.map(t=>"Name: " + t(0)).collect().foreach(println)

通过之前的介绍,读者对支撑结构化数据分析任务的Spark SQL的原理与使用有了一定的了解。在生产环境中,有一类数据分析任务对响应延迟要求高,需要实时处理流数据,在BDAS中,Spark Streaming用于支撑大规模流式处理分析任务。


友情推荐:ABC技术研习社

为技术人打造的专属A(AI),B(Big Data),C(Cloud)技术公众号和技术交流社群。


你可能感兴趣的:(基础架构)