Spark SQL

  • 5.1 连接Spark SQL
  • 5.2 在应用中使用Spark SQL
  • 5.2.1 初始化Spark SQL
  • 5.2.2 基本查询示例
  • 5.2.3 SchemaRDD
  • 5.2.4 缓存
  • 5.3 读取和存储数据
  • 5.3.1 Apache Hive
  • 5.3.2 Parquet
  • 5.3.3 JSON
  • 5.3.4 基于RDD
  • 5.4 JDBC\ODBC服务器
  • 5.4.1 使用Beeline
  • 5.4.2 长生命周期的表与查询
  • 5.5 用户自定义函数
  • 5.5.1 Spark SQL UDF
  • 5.5.2 Hive UDF
  • 5.6 Spark SQL性能
  • 5.6.1 性能调优


  • Spark SQL:是Spark用来操作结构化和半结构化数据的接口
  • Spark SQL三大功能
  • Spark SQL可以从各种结构化数据源(如JSON、Hive、Parquet等)中读取数据
  • Spark SQL支持在Spark程序内使用SQL语句进行数据查询,也支持从外部工具(例如Tableau)通过标准数据库连接器(JDBC\ODBC)连接Spark SQL进行查询。
  • Spark SQL支持SQL与常规的Python\Java\Scala代码高度整合。
  • SchemaRDD:存放Row对象的RDD,每个Row对象代表一行记录,其还包含记录的结构信息(即数据字段),其利用结构信息更加高效地存储数据。
  • Spark SQL用途

5.1 连接Spark SQL

  • Apache Hive是Hadoop上的SQL引擎,Spark SQL编译时能够支持Hive支持,包含Hive支持的Spark SQL可以支持Hive表访问、UDF(用户自定义函数)、SerDe(序列化格式和反序列化格式)、Hive查询语言(HiveQL\HQL).
  • 在Spark SQL中包含Hive的库,并不需要事先安装Hive,只需要在编译Spark SQL时引入Hive支持。
  • Spark SQL编程两个入口(根据是否使用Hive支持)
  • HiveContext:可以提供HiveQL以及其他依赖于Hive的功能的支持,使用HiveContext不需要事先部署好Hive
  • SQLContext: 支持Spark SQL功能的一个子集,子集去掉了需要依赖于Hive的功能。
  • 将Spark SQL连接到一个部署好的Hive上,你需要把hive-site.xml复制到Spark的配置文件目录中(¥SPARK_HOME/conf)

5.2 在应用中使用Spark SQL

5.2.1 初始化Spark SQL
  • 在Scala中,隐式转换被用来把带有类型信息的RDD转变为专门用于Spark SQL查询的RDD(即SchemaRDD)。
// Scala中SQL需要导入的隐式转换支持
 	//创建Spark SQL的HiveContext
 	val hiveCtx = ...
 	// 导入隐式转换支持
 	import hiveCtx._
  • 添加好import 声明猴,就创建出一个HiveContext对象
// 在Scala中创建SQL上下文环境
	val sc = new SparkContext(...)
	val hiveCtx = new HiveContext(sc)
5.2.2 基本查询示例
  • 查询流程:1.读取数据 2. 注册临时表 3.SQL查询
// 在Scala中读取并查询推文数据
  val input = hiveCtx.jsonFile(inputFile)
  //注册输入的SchemaRDD
  input.registerTempTable("tweets")
  //依据retweetCount(转发计数)选出推文
  val topTweets = hiveCtx.sql("SELECT text,retweetCount FROM tweets ORDER BY retweetCount LIMIT 10")
5.2.3 SchemaRDD
  • SchemaRDD是一个由Row对象组成的RDD,附带包含每列数据类型的结构信息。Row对象只是对基本数据类型(如整型和字符串型等)的数组的封装
  • SchemaRDD可以对其使用已有的RDD转化操作(map()、filter()等),可以注册为临时表(registerTempTable()),这样就可以使用HiveContext.sql或SQLContext.sql来对其进行查询
  • SchemaRDD可以存储基本数据类型,也可以存储由这些类型组成的结构体和数组
  • SchemaRDD中可以存储的数据类型
  • 使用Row对象
  • Row对象表示SchemaRDD中的记录,其本质是一个定长的字段数组,在Scala/Java中,Row对象有一系列getter方法,可以通过下标获取每个字段的值。
// 在Scala中访问topTweet这个SchemaRDD中的text列(即第一列)
  val topTweetText = topTweets.map(row => row.getString(0))
5.2.4 缓存
  • Spark SQL会使用一种列式存储格式在内存中表示数据来缓存数据表。
  • HiveQL\SQL语句缓存表
  • CACHE TABLEtableName 缓存表
  • UNCACHE TABLEtableName 删除已有的缓存

5.3 读取和存储数据

  • Spark SQL支持多种结构化数据源(Hive表、JSON、Parquet文件)读取,也可以在程序中通过指定结构信息,将常规RDD转化为SchemaRDD.
5.3.1 Apache Hive
  • Spark SQL支持任何Hive支持的存储格式(SerDe),包括文本文件、RCFiles、ORC、Parquet、Avro以及Protocol Buffer。
  • Spark SQL连接Hive:将hive-site.xml文件复制到Spark的./conf/目录下即可。
// 使用Scala从Hive读取
  //此Hive表有两列,分别为key和Value
  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))
5.3.2 Parquet
  • Parquet 是一种流行的列式存储格式,可以高效地存储具有嵌套字段的记录。Spark SQL可以直接读取和存储Parquet格式文件的方法。
  • HiveContext.parquetFile\SQLContext.parquetFile :读取数据
  • saveAsParquetFile():将SchemaRDD的内容以Parquet的格式保存。
5.3.3 JSON
  • jsonFile():读取JSON文件
// Scala中使用Spark SQL读取JSON数据
  val input = hiveCtx.jsonFile(inputFile)
5.3.4 基于RDD
  • 除了读取数据,也可以基于RDD创建SchemaRDD,在Scala中,带有case class的RDD可以隐式转换为SchemaRDD.
// 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")

5.4 JDBC\ODBC服务器

  • JDBC服务器:是作为一个独立的Spark驱动器运行,可以再多个用户之间共享。任意一个客户端都可以在内存中缓存数据表,对表进行查询。集群的资源以及缓存的数据在所有用户之间都是共享的。这对于让商业智能(BI)工具连接到Spark集群上以及在多用户间共享一个集群的场景非常有用。
  • JDBC服务器通过sbin/start-thriftserver.sh 启动,其会在localhost:10000上进行监听,我们能够通过(HIVE_SERVER2_THRIIFT_PORT和HIVE_SERVER2_THRIFT_BIND_HOST)来修改这些设置,也可以通过Hive配置选项(hive.server2.thrift.port和hive.server2.thrift.bind.host)来修改。
// 启动JDBC服务器
 ./sbin/start-thriftserver.sh --master sparkMaster
  • 启动JDBC服务器时,其会在后台运行并且将所有输出重定向到一个日志文件中。
5.4.1 使用Beeline
  • 在Beeline客户端中,使用标准的HiveQL命令来创建、列举以及查询数据表。
  • CREATE TABLE:创建数据表,LOAD DATA : 进行数据读取
// 读取数据表
 CREATE TABLE IF NOT EXISTS mytable(key INT,value STRING)
 ROW FROMAT DELIMITED FIELDS TERMINATED BY ',';

 LOAD DATA LOCAL INPATH 'learning-spark-examples/files/int_string.csv'
 INTO TABLE mytable;
  • EXPLAIN:查看执行计划
5.4.2 长生命周期的表与查询
  • 使用JDBC服务器的优点:我们可以再多个不同程序之间共享缓存下来的数据表,JDBC Thrift服务器是一个单驱动程序。

5.5 用户自定义函数

5.5.1 Spark SQL UDF
  • 使用Spark支持的编程语言编写好函数,然后通过Spark SQL内建的方法传递进来,就可以注册UDF。
  • 计算字符串长度的非常简单的UDF
// Scala版本的字符串长度UDF
 registerFunction("strLenScala",(_:String).length)
 val tweetLength = hiveCtx.sql("SELECT strLenScala('tweet') FROM tweets LIMIT 10")
5.5.2 Hive UDF
  • Spark SQL若要支持自定义的Hive UDF,则要确保该UDF所在的JAR包已经包含在应用中。
  • 要使用Hive UDF,一定要使用HiveContext.
  • 注册Hive UDF,只需调用hiveCtx.sql(“CTEATE TEMPORARY FUNCTION name AS class.function”)

5.6 Spark SQL性能

  • Spark SQL使有条件的聚合操作变得非常简单。
// Spark SQL多列求和
 	SELECT SUM(user.favouritesCount),SUM(retweetCount),user.id FROM tweets GROUP BY user.id
  • 缓存数据时,Spark SQL使用内存式的列式存储
  • 读取特定的记录时,无需读入整个数据集,然后再筛选;Spark SQL可以把查询语句中的筛选限制条件推到数据存储层,从而大大减少需要读取的数据。
5.6.1 性能调优
  • 性能调优选项
  • 使用JDBC连接接口和Beeline shell时,可以通过set命令来设置包含这些性能选项在内的各种选项
  • 在传统的Spark SQL应用中,可以在Spark配置中设置这些Spark属性
// 在Scala中打开codegen选项的代码
 	conf.set("spark.sql.codegen","true")
  • 调优重点关注选项
  • spark.sql.codegen,这个选项可以让Spark SQL把每条查询语句在运行前编译为Java二进制代码,推荐在大型的或者重复运行的查询中使用codegen,在小规模的查询中不需要使用codegen.
  • spark.sql.inMemoryColumnarStorage.batchSize,这个选项会让Spark SQL按照这个选项指定的大小(默认1000)把记录分组,然后分批次压缩。太小的批处理大小会导致压缩比过低,而批处理大小过大时,也会引发问题。推荐只有在表中记录比较大时,就调低批处理大小来避免内存不够的错误,其它的情况,则使用默认的批处理大小。