Spark1.1.1官网文档翻译5SparkSQL

SparkSQL

 

SparkSQL技术允许使用关系型查询语句SQL、HiveSQL或者在Spark上面执行的scala

这部分的核心是一种新型的RDD-SchemaRDD。SchemaRDD由行对象(Row Objects)以及描述了每一列的数据类型的一个架构组成(Schema)一个Schema类似于传统数据库中的一张表,可以从现有的RDD文件、文件片段、json数据、或者从一个HiveQL来源的单元数据中产生。

 

所有这些例子都包含在Spark distribution中,可以在Spark-shell中运行

 

SparkSQL是一个alpha级别的构成,而我们将会减少一些API方法,这在Spark将来的版本中可能会体现出来

 

快速开始

SparkSQL的入口是一个SQLContext类,或者它的一个子类。创建它你需要一个SparkContext

 

val sc:SparkContext //记得我们说过的shell自带的SparkContext吧

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

 

//createSchemaRDD用于隐式的将RDD转换成为SchemaRDD

import sqlContext.createSchemaRDD

 

除了最基本的SQLContext,你还可以创建一个HiveContext,这是SQLContext提供功能的一个超集。附加的功能包括使用更完整的HiveQL解析器写查询语句的能力,而且能使用HiveUDFS,能够从Hive中读取数据。使用HiveContext你不需要一个现成的Hive安装。和SQLContext处理所有数据源一样,HiveContext只不过是单独封装,在Spark中包含了所有的Hive依赖。如果你使用HiveContext不是问题那么你可以尝试使用Spark1.2以上版本。未来的版本将致力于将SQLContext的特性和HIveContext等同的规模上。

用于解析SQL查询,你可以通过spark.sql.dialect选择特定的形式。这个参数可以使用setConf方法在SQLContext中设置或者使用set命令的key=value形式设置。对于SQLContext,唯一的方式是“sql”。SparkSQL对SQL使用了简单的形式对SQL进行解析,而在HiveContext,默认为“hivesql”,虽然“sql”也可以,但是由于HiveQL解析更完整,所以建议在大多数情况下使用。

 

数据源

SparkSQL通过SchemaRDD接口支持多种数据源。一个SchemaRDD可以像普通RDD一样进行工作,也可以注册为一张临时表。注册一个SchemaRDD临时表允许你运行SQL来查询数据。本节来描述数据加载到SchemaRDD中的各种方法

 

RDDS

SparkSQL使用两种不同的方法将现有的RDD转换成SchemaRDD。第一种方法使用反射来推断RDD所包含对象的具体结构类型。基于反射的方法会导致更加简洁的代码,效果很好,尤其是当你已经知道Schema模式在Spark中的应用之后。

创建SchamaRDD的第二种方法是通过一种编程接口,允许你构建一个Schema,然后将它应用到一个现有的RDD中。虽然这种方法是更加详细的,它允许你构建一个SchemaRDD但是它的列和类型,直到运行时才会知道

 

使用反射机制转换

SparkSQL的scala接口支持将RDD中的实例自动转换到SchemaRDD。这种情况下,类型定义表的架构。类的名字反射作为列的名字使用。类也可以嵌套包含复杂类型,例如Sequences或者Arrays。这些可以隐式转换成为SchemaRDD然后被注册为一张表。表可以用于随后的SQL语句使用

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

 

//隐式转换一个RDD到SchmaRDD

import sqlContext.createSchemaRDD

 

//定义类,注意:scala2.10例子中只支持22个领域,要解决这个限制

//你可以使用自定义类,实现接口

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

 

//创建一个关于Person的RDD并且注册成为table的形式

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

 

people.registerTempTable("people")

 

//SQL语句可以在SQLContext中的sql方法执行

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

 

//SQL查询结果的SchemaRDD支持所有正常的RDD操作,行列中的结果可以被顺序访问

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

 

以编程的方式指定

当类定义不能够被提前(例如,记录结构是一个字符串,编码或者文本数据集将被解析和映射为不同的用户)一个SchemaRDD可以以编程方式通过下面三个步骤创建

1、从原来的RDD创建行

2、创建一个structtype对步骤1中创建的结构进行匹配

3、通过SQLContext的applySchema方法应用这个结果

举例

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

 

//创建RDD

val people = sc.textFile("examples/src/main/resources/people.txt")

 

//schema用一种形式进行编写

val schemaString = "name age"

 

//引入 sparkSQL

import org.apache.spark.sql._

 

//基于map生成Schema形式的字符串模式

val schema = StructType(schemaString.split(" ").map(fieldName =>StructField(firstName,StringType,true)))

 

//将RDD加入行

val rowRDD = people.map(_.split(",")).map(p => Row(p(0),p(1).trim))

 

//将Schema应用到RDD

val peopleSchemaRDD = sqlContext.applySchema(rowRDD,schema)

 

//将SchemaRDD注册为table

peopleSchemaRDD.regiesterTempTable("people")

 

//通过SQLContext执行sql语句

val results = sqlContext.sql("SELECT name FROM people")

 

//结果可以被正常访问

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

 

Parquet文件

Parquet是一种列存储格式,由很多其他的数据处理系统提供支持SparkSQL提供了读写Parquet文件自动保存原始数据的支持

编程形式加载数据

使用从上面的例子中获得的数据

//SQLContext以前的例子,在这个例子中使用

//createSchemaRDD用于将RDD隐式转换成一个SchemaRDD

import sqlContext.createSchemaRDD

 

val people:RDD[Persion]=...//样例类对象,从上个例子引申过来

 

//从上面创建的文件中读取parquet文件,Parquet文件是以自述文件的形式保存的

//加载Parquet文件的结果也是一个SchemaRDD

val parquetFile = sqlContext.parquetFile("people.parquet")

 

//Parquet文件可以注册为表,然后使用SQL语句进行处理

parquetFile.regiesterTempTable("parquetFile")

valteenagers = sqlContext.sql("SELECT name FROM parquetFile WHERE age >=13 AND age <=19")

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

 

配置

配置形式可以使对SQLContext的setConf方法或者在运行SQL时采用SET key=value方式使用

 

名称

默认值

描述

spark.sql.parquet.binaryAsString

false

一些其他的Parquet-producting系统,特别是Impala和旧的SparkSQL版本,二进制和字符串的输出没有什么不同。这个标识告诉SparkSQL提供解释二进制数据文件和字符串文件的兼容性

spark.sql.parquet.cacheMetadata

false

打开Parquet schema的元数据缓存,可以加快静态数据的查询

spark.sql.parquet.compression.codec

snappy

压缩输出Parquet文件,可接受的值:uncompressed(未压缩),snappy,gzip,lzo

 

JSON数据集

SparkSQL可以自动判断出一个JSON数据集并且把它加载成为一个SchemaRDD,这种转换可以通过使用一个SQLContext的两个方法进行

jsonFIle-文件从一个JSON文件每一行作为一个JSON对象进行加载

jsonRDD-从现有的RDD产生,RDD的每个元素包含一个JSON对象字符串

 

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

 

//一个json数据集,path指定了一个文本文件或者一个文本文件文件夹

val path = "examples/src/main/resources/people.json"

 

//从路径中的文件创建一个SchemaRDD

val people = sqlContext.jsonFile(path)

 

//推断模式可以使用printSchema()查看

people.printSchema()

 

//root

//|--age:integer(nullable = true)

//|--name:string(nullable = true)

 

//注册一个SchemaRDD 到table形式

people.registerTempTable("people")

 

//sqlContext的sql方法

val teenagers = sqlContext.sql("SELECT nam FROM people WHERE age >=13 ANDage <= 19")

 

//或者,一个SchemaRDD可以从json数据集创建。RDD[]每一个字符串作为一个json对象

val anotherPeopleRDD = sc.parallelize(

"""{"name":"Yin","address":{"city":"Columbus","state":"Ohio"}}"""::Nil)

valanotherPeople = sqlContext.jsonRDD(anotherPeopleRDD)

 

Hive表

SparkSQL也支持从ApacheHive读写数据。然而,由于Hive有大量的依赖,这些不是Spark的默认组件,为了使用Hive必须首先运行"sbt/sbt -Phive assembly/assembly"(或者使用-Phive for maven)该命令生成一个新的封装,包括Hive,注意这个封装也是必须对所有节点存在都是存在的,因为他们要访问序列化还有反序列化库(SerDes)来存储访问Hive数据

Hive配置文件可以将你的hive-site.xml文件放在./conf中

使用Hive工作时,必须创建一个HiveContext,它继承自SQLContext,并增加了寻找和使用元数据信息还有HiveQL编写的数据查询的支持。无论有没有一个现有的Hive用户都可以创建HiveContext。如果没有配置hive-site.xml上下文会自动在当前目录创建metastore_db和warehouse

 

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

sqlContext.sql("CREATETABLE IF NOT EXISTS src (key INT,value STRING)")

sqlContext.sql("LOADDATA LOCAL INPATH 'examples/src/main/resources/kv1.txt'INTO TABLE src")

 

//查询表示SQL

sqlContext.sql("FROMsrc SELECT key,value").collect().foreach(println)

 

性能优化

对于某些工作负载可以通过在内存中缓存数据来提高性能,或者通过一些实验性的方案。

 

在内存中缓存数据

SparkSQL可以缓存表进行类似内存中的列存储格式,通过调用sqlContext.cacheTable("tableName")使用。然后SparkSQL扫描时候只需要对列进行自动调整压缩来减少内存使用和GC压力。你可以使用sqlContext.uncacheTable("tableName")去删除内存中的表

请注意,如果你调用schemaRDD.cache()而不是sqlContext.cacheTable(...)表将不会被使用列存储格式缓存在内存中。因此,sqlContext.cacheTable()强烈推荐这样使用

配置缓存到内存中,可以使用sqlContext的sqlConf方法或者使用SQL设置key=value方式设置。

属性

默认值

描述

spark.sql.inMemoryColumnarStorage.compressed

false

设置为True时,SparkSQL将自动选择一个基于数据统计的列形式的压缩编码解码器

spark.sql.inMemoryColumnarStorage.batchSize

1000

控制列缓存的批量大小,较大的值可以提高内存的压缩利用率,但是对于缓存面向对象数据来说是冒险的

 

其它配置选项

下列选项也可以被用来调整执行查询性能。一种可能的情况是在未来更多的优化是自动执行的

属性

默认值

描述

spark.sql.autoBroadcastJoinThreshold

10000

配置信息将被广播加入到所有节点,worker node上面所执行字节的最大大小。通过将该值设置为-1时候广播可以被禁用。值得注意的是,目前统计数据仅支持Hive元数据创建表指令'ANALYZE TABLE <tableName> COMPUTE STATISTIS noscan'的运行

spark.sql.codegen

false

设置为true时,代码将在一个特定的查询表达式值的运行时生成。对于某些查询的复杂表达式,这个选项可以导致显著的speed-ups然而,简单的查询,这样做可能导致查询缓慢。

spark.sql.shuffle.partitions

200

配置join和聚合操作时候shuffle的分区数量

 

其它SQL接口

Spark运行SQL查询时候不需要编写任何代码接口

 

运行Thrift服务

ThriftJDBC服务实现了对于Hive0.12的HiveServer2。你可以测试带有Spark和Hive0.12的JDBC服务脚本

开启JDBC服务,运行以下脚本

 

./sbin/start-thriftserver.sh

这个脚本接受所有bin/spark-submit命令执行选项,再加上一个--hiveconf选项指定Hive的性质。你可以运行./sbin/start-thriftserver.sh--help来查看一个完整的选项列表。默认情况下,服务器监听localhost:10000你可以通过环境变量重写这个行为:

 

export HIVE_SERVER2_THRIFT_PORT=<listening-port>

export HIVE_SERVER2_THRIFT_BIND_HOST=<listening-host>

./sbin/start-thriftserver.sh\

--master<master-uri>

...

或者系统属性

./sbin/start-thriftserver2.sh\

--hiveconfhive.server2.thrift.port=<listening-port>

--hiveconfhive.server2.thrift.bind.host=<listening-host>

--master<master-uri>

...

 

现在你可以直接连接Thrift JDBC来测试

 

./bin/beeline

 

连接JDBCServer

 

beeline>! connect jdbc:hive2://localhost:10000

 

beeline会要求你输入用户名密码,在非安全模式,只要在安全模式输入用户名和密码。

配置文件hive-site.xml在 conf下面

你也可以直接使用来自hive的脚本。

 

使用SparkSQL CLI

SparkSQLCLI是一个方便的工具,可以在本地模式下使用Hive元数据服务并且从命令行输入查询。请注意,SparkSQLCLI不能使用ThriftJDBC服务。

启动SQLCLI,从以下目录运行

 

./bin/spark-sql

 

Hive参数是在conf目录下的hive-site.xml文件设置,你可以使用./bin/spark-sql --help查看所有可以使用的选项列表

 

与其它系统的兼容性

Shark的用户迁移指南

调度

为一个JDBC客户端会话建立一个公平的调度器,用户可以设置spark.sql.thriftserver.scheduler.pool变量

 

SET spark.sql.thriftserver.scheduler.pool=accounting;

 

reduce数量

Shark中,默认的Reduce是1由mapred.reduce.tasks控制。SparkSQL不使用这个属性但有另一个属性spark.sql.shuffle.partitions默认值是200,用户可以通过设置自定义属性

 

SET spark.sql.shuffle/partitions=10;

SELECT page,count(*) c FROM logs_last_month_cached GROUP BY page ORDER BY c DESC LIMIT 10;

 

你可以把这个属性在hive-site.xml中设置以覆盖默认值。

现在,这个mapred.reduce.tasks依然是公认的,并自动转化为spark.sql.shuffle.partitions

 

缓存

shark.cache这个属性不再存在,并与_cached名字的表一起不再被缓存。相反,我们提供缓存表CACHE TABLE和UNCACHE TABLE语句让用户控制表进行缓存

 

CACHE TABLE logs_last_month;

UNCACHE TABLE logs_last_month;

 

注意:CACHE TABLE是惰性的,类似于在RDD进行缓存,这个命令是作为一个标志tbl来确保分区缓存,当计算没有触及到的时候,不进行缓存,当使用到的时候,则进行缓存,你可以简单地进行表计数缓存之后立即执行:

 

CACHE TABLE logs_last_month;

SELECT COUNT(1) FROM logs_last_month;

 

一些缓存相关的特征还不支持以下几方面:

*用户定义的分区级缓存策略

*RDD重装

*在内存中缓存写策略

 

ApacheHive的兼容性

SparkSQL的设计是对Hive元数据仓库进行兼容,SerDes和UDFS。目前Spark是基于Hive0.12.0版本

 

现有的Hive部署

SparkSQL的JDBC服务设计初衷是“开箱”与现有Hive进行兼容。你不需要修改现有的Hive元数据或者更改你的现有表分区

 

对Hive的支持

Spark支持绝大多数的Hive功能,例如:

Hive查询语句,包括 select, group by, order by, cluster by, sort by

所有的Hive符号

  关系符:=,==<><>>=<=等等?)

  计算符:+-*/%等等

  逻辑操作符:AND,&&,OR,||,等等

  复合类型操作

  数学函数:sign,ln,cos等等

  字符函数:instr,length,printf等等

用户自定义函数(UDF)

用户自定义聚合函数(UDAF)

用户自定义序列化格式(SerDes)

Joinsjoin,{left|right|full}outer join,left semi join,cross join

Unions

子查询

  select col from (select a+b as col from t1)t2

抽样(sampling)

解释(Explain)

分区表(partitions table)

所有的HiveRDD操作包括create table,create table as select,alter table

众多的Hive类型,包括tinyint,smallint,int,bigint,boolean,float,double,string,binary,timestamp,array<>,map<>,struct<>

 

不支持的Hive功能

下面是一个Spark不支持的Hive功能列表,绝大多数是很少使用的功能

主要的特性

  Spark不支持插入形式的动态分区表

  分桶表或者hash分区,Spark不支持分桶

深层的特性

  使用不同输入格式的分区表,SparkSQL需要表分区需要有相同的输入格式

  非等外连接,在一种罕见的情况下,使用外连接与非等值的连接条件(例如key<10)Spark将给出错误的结果为null的元组

  union和日期类型

  union join

  一次查询多次插入(single query multi insert)

  列统计信息的收集,SparkSQL不会背负所收集的保存在hive元数据的列统计信息

 

Hive 输入、输出格式化

  对于cli的输出,结果显示回cli,SparkSQL只支持TextOutputFormat

  Hadoop存档格式(archive)

 

Hive优化

少数的Hive优化是不包含在Spark中的,其中的一些(比如索引)是不太重要的Spark的计算模型,其他的比如槽位的释放等

 

块位图索引(Block level bitmap indexes)以及虚拟索引(virtualcolumns)用于建立索引

自动将map join,join一个大表与多个小表,Hive会自动将其转换为一个map join,我们将在下一个版本进行自动转换

自动确定连接和reducer、join、groupby数量目前在SparkSQL,你需要控制任务并行度,然后用shuffle调用“SET spark.sql.shuffle.partitions=[num_tasks];”

元数据查询,仅返回元数据结果,SparkSQL还推出任务计算结果

倾斜数据标志,SparkSQL不支持Hive的数据倾斜标志

合并多个小文件的查询结果,如果结果输出包含多个小文件,Hive可以合并小的文件到大文件,避免溢出HDFS元数据,SparkSQL不支持

 

关系查询语句

关系查询语句(Language-Integrated queries),目前只支持Scala

SparkSQL还支持特定于域的语言编写查询,再一次,使用上次的示例

 

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

import sqlContext._

val people:RDD[Person]=...

//结果类似于“SELECT nameFROM people WHERE age >= 10 AND age <=19”

val teenagers = people.where('age >= 10).where('age <=19).select('name)

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

 

DSL使用Scala基础符号表中的列,这个标示符是带有标识的(')这些符号表达式将被隐式转换,通过SQL执行引擎进行解析。一个完整的功能支持列表可以参考scaladoc实现

 

Spark的数据类型参考

所有数据类型都在org.apache.spark.sql包中,你可以进行导入

 

import org.apache.spark.sql._

 

类型

Scala类型

API访问或创建

ByteType

Byte

ByteType

ShortType

Short

ShortType

IntegerType

Int

IntegerType

LongType

Long

LongType

FloatType

Float

FloatType

DOubleType

Double

DoubleType

DecimalType

Scala.math.sql.BigDecimal

DecimalType

StringType

String

StringType

BinaryType

Array[Byte]

BinaryType

BooleanType

Boolean

BooleanType

TimestampType

java.sql.Timestamp

TimeStampType

ArrayType

scala.collection.Seq

ArrayType(elementType,[containsNull])注意:默认值containsNull为false

MapType

scala.collection.Map

MapType(keyType,valueType,[calueContainsNull])注意:valueContainsNull默认值为NULL

StructType

org.apache.spark.sql.Row

StructType(fields)注意:fields是一个序列的结构域,两个域不允许拥有相同名称

StructField

在该字段的Scala值类型(例如,整型数据类型为一个integerType的StructField)

StructField(name,dataType,nullable)

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值