在很多情况下,开发人员并不了解Scala语言,也不了解Spark常用的API,但又非常想要使用Spark框架提供的强大的数据分析能力。Spark的开发工程师们考虑到了这个问题,于是利用SQL语言的语法简洁、学习门槛低以及在编程语言中普及程度和流行程度高等诸多优势,开发了Spark SQL模块。通过Spark SQL,开发人员能够使用SQL语句实现对结构化数据的处理。
本节主要介绍什么是Spark SQL、Spark SQL的特点、什么是DataFrame和什么是DataSet。
Spark SQL是Spark用于结构化数据(Structured Data)处理的Spark模块。与基本的Spark RDD API不同,Spark SQL的抽象数据类型为Spark提供了关于数据结构和正在执行的计算的更多信息。在内部,Spark SQL使用这些额外的信息去做一些优化。
有多种方式与Spark SQL进行交互,比如SQL和Dataset API。当计算结果的时候,这些接口使用相同的执行引擎,不依赖正在使用哪种API或者语言。这种统一也就意味着开发者可以很容易在不同的API之间进行切换,这些API提供了最自然的方式来表达给定的转换。
Hive是将Hive SQL转换成MapReduce,然后提交到集群上执行,大大简化了编写MapReduce程序的复杂性,但是MapReduce这种计算模型执行效率比较慢,所以Spark SQL应运而生,它将Spark SQL转换成RDD,然后提交到集群上执行,执行效率非常高。
Spark SQL提供了以下2个编程抽象,类似Spark Core中的RDD。
- DataFrame。
- DataSet。
(1)Integrated(易整合):Spark SQL无缝地整合了SQL查询和Spark编程。
(2)Uniform Data Access(统一的数据访问方式):Spark SQL使用相同的方式连接不同的数据源。
(3)Hive Integration(集成 Hive):Spark SQL在已有的仓库上直接运行SQL或者HiveQL。
(4)Standard Connectivity(标准的连接方式):Spark SQL通过JDBC或者ODBC来连接。
与RDD类似,DataFrame也是一个分布式数据容器。
然而DataFrame更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即schema。
同时,DataFrame与Hive类似,也支持嵌套数据类型(struct、array和map)。
从API易用性的角度上看,DataFrame API提供的是一套高层的关系操作,比函数式的RDD API要更加友好、门槛更低。
DataFrame与RDD的区别如图4-5所示。
图4-5
图中左侧的RDD[Person]虽然以Person为类型参数,但Spark框架本身不了解Person类的内部结构。而右侧的DataFrame却提供了详细的结构信息,使得Spark SQL可以清楚地知道该数据集中包含哪些列,每列的名称和类型各是什么。
DataFrame为数据提供了schema视图,可以把它当作数据库中的一张表来对待。
DataFrame也是懒执行的,性能上比RDD要高,主要原因在于优化的执行计划—查询计划通过Spark Catalyst Optimiser(Catalyst优化器,基于Scala的函数式编程结构设计的可扩展优化器)进行优化。
为了说明查询优化,我们来看图4-6展示的人口数据分析的示例。
图4-6
图中构造了两个DataFrame,将它们join之后又做了一次filter操作。
如果原封不动地执行这个计划,最终的执行效率不是很高,因为join是一个代价较大的操作,也可能会产生一个较大的数据集。如果我们能将filter下推到join下方,先对DataFrame进行过滤,再join过滤后的较小的结果集,便可以有效缩短执行时间。而Spark SQL的查询优化器正是这样做的。简而言之,逻辑查询计划优化就是一个利用基于关系代数的等价变换,将高成本操作替换为低成本操作的过程。
(1)DataSet是DataFrame API的一个扩展,也是Spark SQL最新的数据抽象(1.6版本新增)。
(2)用户友好的API风格,既具有类型安全检查,也具有DataFrame的查询优化特性。
(3)Dataset支持编解码器,当需要访问非堆上的数据时可以避免反序列化整个对象,提高了效率。
(4)样例类被用来在DataSet中定义数据的结构信息,样例类中每个属性的名称直接映射到DataSet中的字段名称。
(5)DataFrame是DataSet的特列,DataFrame=DataSet[Row],所以可以通过as方法将DataFrame转换为DataSet。Row是一个类型,跟Car、Person这些类型一样,所有的表结构信息都用Row来表示。
(6)DataSet是强类型的,比如可以有DataSet[Car]、DataSet[Person]等。
(7)DataFrame只是知道字段,但是不知道字段的类型,所以在执行这些操作时是没办法在编译的时候检查字段类型是否正确,比如我们对一个字符串进行减法操作,在执行的时候才报错。而DataSet不仅知道字段,还知道字段类型,所以有更严格的错误检查。
本文摘自《Spark入门与大数据分析实战》,获出版社和作者授权发布。