Spark SQL提供三大功能:
1、可以从各种结构化数据源(例如JSON、Hive、Parquet等)中读取数据
2、Spark SQL不仅支持在Spark程序内使用SQL语句进行数据查询,也支持外部工具链接SQL进行查询。
3、支持SQL与常规python/java/scala代码高度整合,包括连接RDD与SQL表、公开的自定义SQL函数接口。
SchemaRDD
用来存放ROw对象的RDD,每个ROW对象代表一行记录。SchemaRDD还包含记录的结构信息(即数据字段)。SchemaRDD支持SQL查询。
一、连接Spark SQL
引入Spark SQL 需要添加一些额外依赖,这种分离机制使得Spark内核的编译无需依赖大量额外的包
Apache Hive 是 Hadoop上的SQL搜索引擎,Spark SQL编译时可以包含Hive支持,也可以不包含。Hive支持的Spark SQL支持:Hive表访问,UDF(用户自定义函数)、SerDe(序列化格式和反序列化格式)以及Hive查询语言。
初始化Spark SQL
Scala中SQL的import生命
// 导入Spark SQL
import org.apache.spark.sql.hive.HiveContext
// 如果不能使用hive依赖的话
import org.apache.spark.sql.SQLContext
在创建出HiveContext的实例之后,需要加入必要的隐式转换。
//在Scala中SQL需要导入隐式转换支持
//创建Spark SQL 的HiveContext
val hiveCtx = ...
//导入隐式转换支持
import hiveCtx._
添加好import 声明后,需要创建一个HiveContext对象。如果无法引入Hive依赖,就创建出一个SQLContext对象作为SQL的上下文环境。这两个类都需要传入一个SparkContext对象作为运行的基础。
//在Scala中创建SQL上下文环境
val sc = new SparkContext(...)
val hiveCtx = new HiveContext(sc)
基本查询示例
//从JSON中读取数据
val input = hiveCtx.jsonFile(inputFile)
//注册输入的数据为tweets
input.registerTempTable("tweets")
//依据retweetCount(转发计数)选出推文
val topTweets = hiveCtx.sql("SELECT text,retweetCount FROM tweets ORDER BY retweetCount LIMIT 10")
SchemaRDD(DATAFrame)
1、读取数据和执行操作都会返回SchemaRDD,SchemaRDD是一个由Row对象组成的RDD,附带包含每列数据类型的结构信息。
2、SchemaRDD是RDD,可以对其应用已有的RDD转换操作,比如map()和filter()
3、可以把SchemaRDD注册为临时表,使用HiveContext.sql或SQLContext.sql对其进行查询。可以通过ScheamRDD的registerTempTable进行注册。
4、SchemaRDD可以存储的数据类型
Spark SQL/HiveQL类型 | Scala类型 | Python类型 |
---|---|---|
TINYINT | Byte | int/Long(-128 ~ 127) |
SMALLINT | Short | int/long(-32768 ~ 32767) |
INT | Int | int/Long |
BIGINT | Long | long |
FLOAT | Float | float |
DOUBLE | Double | float |
DECIMAL | scala.math.BigDecimal | decimal.Decimal |
STRING | String | string |
BINARY | Array[Byte] | bytearray |
BOOLEAN | Boolean | bool |
TIMESTAMP | java.sql.TimeStamp | date.datetime |
ARRAY | Seq | list、tuple或array |
MAP | Map | dict |
STRUCT | Row | Row |
COL1_TYPE,…> |
COL1_TYPE,…>为结构体,在Spark SQL中直接被表示为其他的Row对象。所有这些复杂类型都可以互相嵌套。
Row对象可以使用get函数,将给定序号,返回一个Object类型并转化
//在Scala中访问topTweet这个SchemaRDD中的text列
val topTweetText = topTweets.map(row => row.getString())
缓存
为了节省内存,SQL使用hiveCtx.cacheTable(“tableName”)方法, 缓存数据。缓存下来的表只会在驱动器程序的生命周期保留在内存中,如果驱动器进程退出吗,就需要重新缓存数据。
命令:CACHE TABLEtablename 缓存表格/ UNCACHE TABLEtablename 删除已有缓存
读取和存储数据
SQL 在读取数据时,可以只读取需要数据
Apache Hive
当从Hive中读取数据时,Spark SQL支持任何Hive支持的存储格式(SerDe),包括文本文件、RCFiles、ORC、Parquet、Avro以及Protocol Buffer。
使用scala从Hive读取
import org.apache.spark.sql.hive.HiveContext
val hiveCtx = new HiveContext(sc)
val rows = hiveCtx.sql(SELECT key, value FROM mytable)
val keys = rows.map(row => row.getInt(0))
Parquet
Parquet可以高效存储具有嵌套字段的记录。Parquet常在Hadoop生态圈中被使用,他也支持Spark SQL的全部数据类型。
//python中的Parquet数据读取
# 从一个有name和favouriteAnimal字段的Parquet文件中读取数据
rows = hiveCtx.parquetFile(parquetFile)
names = rows.map(lambda row: row.name)
print "Everyone"
print names.collect(
#Python中的Parquet数据查询
# 寻找熊猫爱好者
tbl = rows.registerTempTable("people")
pandaFriends = hiveCtx.sql("SELECT name FROM people WHERE favouriteAnimal =
\"panda\"")
print "Panda friends"
print pandaFriends.map(lambda row: row.name).collect()
#Python中Parquet文件保存
pandaFriends.saveAsParquetFile("hdfs://...")
JSON
#Scala中读取JSON数据
val input = hiveCtx.jsonFile(inputFile)
#用SQL查询嵌套数据以及数组元素
select hashtagEntities[0].text from tweets LIMIT 1;
基于RDD
DATAFrame可以基于RDD创建
//在Scala中基于case class 创建SchemaRDD
case class HappyPerson(handle: String, favouriteBeverage: String)
...
// 创建了一个人的对象,并且把它转成SchemaRDD
val happyPeopleRDD = sc.parallelize(List(HappyPerson("holden", "coffee")))
// 注意:此处发生了隐式转换
// 该转换等价于sqlCtx.createSchemaRDD(happyPeopleRDD)
happyPeopleRDD.registerTempTable("happy_people")
JDBC/ODBC 服务器
Spark SQL 也提供JDBC链接支持
使用Beeline
在Beeline客户端中,你可以使用标准的HiveQL命令来创建、列举以及查询数据表。
# 读取数据表
> CREATE TABLE IF NOT EXISTS mytable (key INT, value STRING)
ROW FORMAT DELIMITED FIELDS TERMINATED BY‘,’;
> LOAD DATA LOCAL INPATH‘learning-spark-examples/files/int_string.csv’
INTO TABLE mytable
#列举数据表
> SHOW TABLES;
mytable
Time taken: 0.052 second
Spark SQL shell执行EXPLAIN
spark-sql> EXPLAIN SELECT * FROM mytable where key = 1;
== Physical Plan ==
Filter (key#16 = 1)
HiveTableScan [key#16,value#17], (MetastoreRelation default, mytable, None), None
Time taken: 0.551 seconds
用户自定义函数
用户自定义函数,也叫做UDF,可以让用户注册自定义函数,并在SQL中调用。
Spark SQL UDF
可以使用Spark支持的编程语言编写好函数,然后通过Spark SQL内建的方法传递进来,非常便捷的注册UDF,scala和python中,可以利用语言原生的函数和lambda语法支持,在Java中,需要扩展对应的UDF类。
//Scala版本的字符串长度UDF
registerFunction("strLenScala", (_: String).length)
val tweetLength = hiveCtx.sql("SELECT strLenScala('tweet') FROM tweets LIMIT 10")
Spark SQL 也支持 Hive UDF,标准的Hive UDF已经自动包含在Spark SQL中。如需支持自定义Hive UDF,需要确保UDF的JAR包已经包含在应用中。
如果使用Hive UDF,应该使用Hive Context,调用hiveCtx.sql(“CREATE TEMPORARY FUNCTION name AS class.function”)
Spark SQL性能
谓词下推 可以让Spark 如果低层数据存储支持只读取一个范围内的记录,SQL将查询中的一部分工作“下移”到查询引擎上,从而大大减少需要读取的数据。
选项 | 默认值 | 用途 |
---|---|---|
spark.sql.codegen | false | 设为true时,会把查询语句编译为Java二进制代码,提高大型查询的性能,但小效果查询时会变慢 |
spark.sql.inMemoryColummarStorage.compressed | false | 自动对内存中的列式存储进行压缩 |
spark.sql.inMemoryColumnarStorage.batchSize | 1000 | 列式存储的每个批处理的大小 |
spark.sql.parquet.compression.codec | snappy | 使用哪种编码器,选项包括:uncompress/snappy/gzip/lzo |
//在scala中打开codegen选项的代码
conf.set("spark.sql.codegen","true")