SparkSQL简介


日期

版本

修订

审批

修订说明

2016.10.20

1.0

章鑫8

 

初始版本

 

 

 

 

 



1        简介

SparkSQL是Spark的一个组件,用于结构化数据的计算,SparkSQL提供了一个称为DataFrames的编程抽象,DataFrames可以充当分布式SQL查询引擎。

与SparkSQL紧密相关的组件是Shark和hive,其中Shark已经被开发者摒弃。

 

2        开发背景

2.1  Shark和hive

SparkSQL的前身是Shark,给熟悉RDBMS但又不理解MapReduce的技术人员提供快速上手的工具,Hive应运而生,它是当时唯一运行在Hadoop上的SQL-on-Hadoop工具。但是MapReduce计算过程中大量的中间磁盘落地过程消耗了大量的I/O,降低的运行效率,为了提高SQL-on-Hadoop的效率,大量的SQL-on-Hadoop工具开始产生,其中表现较为突出的是:

Ø  MapR的Drill

Ø  Cloudera的Impala

Ø  Shark

其中Shark是伯克利实验室Spark生态环境的组件之一,它修改了下图1所示的右下角的内存管理、物理计划、执行三个模块,并使之能运行在Spark引擎上,从而使得SQL查询的速度得到10-100倍的提升。

1 hiveShark框架


2.2  Shark和SparkSQL

随着Spark的发展,对于野心勃勃的Spark团队来说,Shark对于Hive的太多依赖(如

采用Hive的语法解析器、查询优化器等等),制约了Spark的One Stack Rule Them All的既定方针,制约了Spark各个组件的相互集成,所以提出了SparkSQL项目。SparkSQL抛弃原有Shark的代码,汲取了Shark的一些优点,如内存列存储(In-Memory ColumnarStorage)、Hive兼容性等,重新开发了SparkSQL代码;由于摆脱了对Hive的依赖性,SparkSQL无论在数据兼容、性能优化、组件扩展方面都得到了极大的方便。

Ø  数据兼容方面,不但兼容Hive,还可以从RDD、parquet文件、JSON文件中获取数据,未来版本甚至支持获取RDBMS数据以及cassandra等NOSQL数据;

Ø  性能优化方面,除了采取In-Memory ColumnarStorage、byte-codegeneration等优化技术外、将会引进CostModel对查询进行动态评估、获取最佳物理计划等等;

Ø  组件扩展方面,无论是SQL的语法解析器、分析器还是优化器都可以重新定义,进行扩展。

2014年6月1日Shark项目和SparkSQL项目的主持人Reynold Xin宣布:停止对Shark的开发,团队将所有资源放SparkSQL项目上,至此,Shark的发展画上了句话,但也因此发展出两个直线:SparkSQL和Hive on Spark。

                                                                                      

图2

其中SparkSQL作为Spark生态的一员继续发展,而不再受限于Hive,只是兼容Hive;而Hive on Spark是一个Hive的发展计划,该计划将Spark作为Hive的底层引擎之一,也就是说,Hive将不再受限于一个引擎,可以采用Map-Reduce、Tez、Spark等引擎。


3       技术概念

相比于Spark RDD API,Spark SQL包含了对结构化数据和在其上的运算的更多的信息,

Spark SQL使用这些信息进行了额外的优化,使对结构化数据的操作更加高效和方便。

       有多种方式去使用SparkSQL,包括SQL、DataFrames API和Datasets API。但无论是哪种API或者是编程语言,它们都是基于同样的执行引擎,因此你可以在不同的API之间随意切换,它们各有各的特点。

.

3.1  SQL

使用SparkSQL的一种方式就是通过SQL语句来执行SQL查询。当在编程语言中使用

SQL时,其返回结果将被封装为一个DATAFrames。

 

3.2  DataFrame

DataFrame是一个分布式集合,其中数据被组织为命名的列。它概念上等价于关系数

据库中的表,但底层做了更多的优化。DataFrame可以从很多数据源构建,比如:已经存在的RDD、结构化的文件、外部数据库、Hive表等。

       DataFrame的前身是SchemaRDD,从Spark 1.3.0开始SchemaRDD更名为DataFrame。与SchemaRDD的主要区别是:DataFrame不再直接继承自RDD,而是自己实现了RDD的绝大多数功能。你仍旧可以在DataFrame上调用.rdd方法将其转换为一个RDD。RDD可看做是分布式的对象的集合,Spark并不知道对象的详细信息,DataFrame可看做是分布式的Row对象的集合,其提供了由列组成的详细信息,使得SparkSQL可以进行某些形式的执行优化。DataFrame和普通的RDD的逻辑框架区别如下所示:

                                                       

                                              图3 RDD和DataFrame
DataFrame不仅比RDD有更加丰富的算子,更重要的是它可以进行执行计划优化(得益于CatalystSQL解析器),另外Tungsten项目给DataFrame的执行效率带来了很大提升(不过Tungsten优化也可能在后续开发中加入到RDD API中)。
但是在有些情况下RDD可以表达的逻辑用DataFrame无法表达,所以后续提出了Dataset API,Dataset结合了RDD和DataFrame的好处。


3.3  Dataset

Dataset是Spark 1.6新添加的一个实验性接口,其目的是想结合RDD的好处(强类

型(这意味着可以在编译时进行类型安全检查)、可以使用强大的lambada函数)和SparkSQL的优化执行引擎的好处。可以从JVM对象构造出Dataset,然后使用类似于RDD的函数式转换算子(map/flatMap/filter等)对其进行操作。

       Dataset通过Encoder实现了自定义的序列化格式,使得某些操作可以在无需序列化的情况下直接进行。另外Dataset还进行了包括Tungsten优化在内的很多性能方面的优化。

       实际上Dataset是包含了DataFrame的功能的,这样二者就出现了很大的冗余,故Spark 2.0将二者统一:保留Dataset API,把DataFrame表示为Dataset[Row],即Dataset的子集。

 

3.4  API进化

Spark在迅速的发展,从原始的RDD API,再到DataFrame API,再到Dataset的出现,

执行性能上有了很大的提升。

       我们在使用API时,应该优先选择DataFrames和Dataset,因为这二者的性能很好,而且以后的优化它都可以享受到,但是为了兼容早期版本的程序,RDD API也会一直保留着。后续Spark上层的库将全部会用DataFrames和Dataset,比如MLlib、Streaming、Graphx等。

 

3.5  SparkSQL的数据源

SparkSQL支持通过SchemaRDD接口操作各种数据源。一个SchemaRDD能够作为一个

一般的RDD被操作,也可以被注册为一个临时的表。注册一个SchemaRDD为一个表就可以允许你在其数据上运行SQL查询。

       加载数据为SchemaRDD的多种数据源,包括RDDs、parquet文件(列式存储)、JSON数据集、Hive表,以下主要介绍将RDDs转换为schemaRDD的两种方法。

(1)       利用反射推断模式

使用反射来推断包含特定对象类型的RDD的模式(schema)。适用于写Spark程序的同

时,已经知道了模式,使用反射可以使得代码简洁。结合样本的名字,通过反射读取,作为列的名字。这个RDD可以隐式转化为一个schemaRDD,然后注册为一个表。表可以在后续的sql语句中使用。


val sqlContext = new org.apache.spark.sql.SQLContext(sc)
import sqlContext.implicits._
case class Person(name:String,age:Int)
val people = sc.textFile("file:///home/hdfs/people.txt").map(_.split(",")).map(p => Person(p(0),p(1).trim.toInt)).toDF()
people.registerTempTable("people")
 
val teenagers = sqlContext.sql("SELECT name,age FROM people WHERE age>= 19 AND age <=30")
teenagers.map(t => "Name:"+t(0)).collect().foreach(println)
 
teenagers.map(t => "Name:" + t.getAs[String]("name")).collect().foreach(println)
teenagers.map(_.getValueMap[Any](List("name","age"))).collect().foreach(println)

(2)编程指定模式

通过一个编程接口构造模式来实现,然后可在存在的RDDs上使用它。适用于当前样本模式未知一个SchemaRDD可以通过三步来创建。

Ø  从原来的RDD创建一个行的RDD

Ø  创建由一个StructType表示的模式与第一步创建的RDD的行结构相匹配

Ø  在行RDD上通过applySchema方法应用模式

val people = sc.textFile("file:///home/hdfs/people.txt")
val schemaString = "name age"
 
import org.apache.spark.sql.Row;
import org.apache.spark.sql.types.{StructType,StructField,StringType};
 
val schema = StructType(schemaString.split(" ").map(fieldName => StructField(fieldName,StringType,true)))
 
val rowRDD = people.map(_.split(",")).map(p => Row(p(0),p(1).trim))
 
val peopleSchemaRDD = sqlContext.applySchema(rowRDD,schema)
peopleSchemaRDD.registerTempTable("
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值