概况
Spark SQL是用于结构化数据处理的Spark模块。与基本的Spark RDD API不同,Spark SQL提供的接口为Spark提供了关于数据结构和正在执行的计算的更多信息。在内部,Spark SQL使用这些额外的信息来执行额外的优化。有几种与Spark SQL进行交互的方式,包括SQL和Dataset API。在计算结果时,使用相同的执行引擎,而不管使用哪种API /语言表示计算。这种统一意味着开发人员可以轻松地在不同的API之间来回切换,基于这些API提供了表达给定转换的最自然的方式。
SQL
Spark SQL的一个用途是执行SQL查询。Spark SQL也可以用来从现有的Hive安装中读取数据。有关如何配置此功能的更多信息,请参阅Hive表部分。从另一种编程语言中运行SQL时,结果将作为数据集/数据框返回。您还可以使用命令行 或通过JDBC / ODBC与SQL接口进行交互。
DataFrames
DataFrame是分布式数据集合,组织到命名列中。它的作用上相当于关系数据库中的表格或R / Python中的DataFrames,但具有更丰富的优化。DataFrame可以从各种各样的源构建,例如:结构化数据文件,Hive中的表,外部数据库或现有的RDD。相对于Rdd,DataFrames具有更好的性能优化。spark sql的查询结果最后以DataFrames的形式返回
从Rdd转换为DataFrames有两种方式:
- 利用反射推断模式
- 编程指定模式
利用反射推断模式
package sql
import org.apache.log4j.{Level, Logger}
import org.apache.spark.{SparkContext, SparkConf}
/**
* Created by Administrator on 2018/1/12.
*/
object RddToDf_1 {
case class Person(name: String, age: Int)
def main(args: Array[String]) {
// 屏蔽不必要的日志显示在终端上
Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
val conf = new SparkConf().setAppName("RddToDf_1").setMaster("local")
val sc = new SparkContext(conf)
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
// this is used to implicitly convert an RDD to a DataFrame.
import sqlContext.implicits._
// Define the schema using a case class.
// Note: Case classes in Scala 2.10 can support only up to 22 fields. To work around this limit,
// you can use custom classes that implement the Product interface.
// // Create an RDD of Person objects and register it as a table.
val people = sc.textFile("E:\\wgz\\hadoop\\Spark\\SparkLearning\\data\\people.txt").map(_.split(",")).map(p => Person(p(0), p(1).trim.toInt)).toDF()
people.registerTempTable("people")
// SQL statements can be run by using the sql methods provided by sqlContext.
val teenagers = sqlContext.sql("SELECT name, age FROM people WHERE age >= 13 AND age <= 19")
// The results of SQL queries are DataFrames and support all the normal RDD operations.
// The columns of a row in the result can be accessed by field index:
teenagers.map(t => "Name: " + t(0)).collect().foreach(println)
// or by field name:
teenagers.map(t => "Name: " + t.getAs[String]("name")).collect().foreach(println)
// row.getValuesMap[T] retrieves multiple columns at once into a Map[String, T]
teenagers.map(_.getValuesMap[Any](List("name", "age"))).collect().foreach(println)
// Map("name" -> "Justin", "age" -> 19)
}
}
编程指定模式
import org.apache.log4j.{Level, Logger}
import org.apache.spark.{SparkContext, SparkConf}
/**
* Created by Administrator on 2018/1/12.
*/
object RddToDf_1 {
case class Person(name: String, age: Int)
def main(args: Array[String]) {
// 屏蔽不必要的日志显示在终端上
Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
val conf = new SparkConf().setAppName("RddToDf_1").setMaster("local")
val sc = new SparkContext(conf)
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
// this is used to implicitly convert an RDD to a DataFrame.
import sqlContext.implicits._
// Define the schema using a case class.
// Note: Case classes in Scala 2.10 can support only up to 22 fields. To work around this limit,
// you can use custom classes that implement the Product interface.
// // Create an RDD of Person objects and register it as a table.
val people = sc.textFile("E:\\wgz\\hadoop\\Spark\\SparkLearning\\data\\people.txt").map(_.split(",")).map(p => Person(p(0), p(1).trim.toInt)).toDF()
people.registerTempTable("people")
// SQL statements can be run by using the sql methods provided by sqlContext.
val teenagers = sqlContext.sql("SELECT name, age FROM people WHERE age >= 13 AND age <= 19")
// The results of SQL queries are DataFrames and support all the normal RDD operations.
// The columns of a row in the result can be accessed by field index:
teenagers.map(t => "Name: " + t(0)).collect().foreach(println)
// or by field name:
teenagers.map(t => "Name: " + t.getAs[String]("name")).collect().foreach(println)
// row.getValuesMap[T] retrieves multiple columns at once into a Map[String, T]
teenagers.map(_.getValuesMap[Any](List("name", "age"))).collect().foreach(println)
// Map("name" -> "Justin", "age" -> 19)
}
}
数据源
sparksql的数据大致分为以下四种
- RDDs
- parquet文件
- JSON数据集
- Hive表
RDDs
详情可以参考rdd To DataFrames
val people = sc.textFile("E:\\wgz\\hadoop\\Spark\\SparkLearning\\data\\people.txt").map(_.split(",")).map(p => Person(p(0), p(1).trim.toInt)).toDF()
people.registerTempTable("people")
// SQL statements can be run by using the sql methods provided by sqlContext.
val teenagers = sqlContext.sql("SELECT name, age FROM people WHERE age >= 13 AND age <= 19")
parquet
parquet可以加速查询,因为它只检查所有需要的列并对它们的值执行计算,因此只读取一个数据文件或表的小部分数据。Parquet 还支持灵活的压缩选项,因此可以显著减少磁盘上的存储。
如果您在 HDFS 上拥有基于文本的数据文件或表,而且正在使用 Spark SQL 对它们执行查询,那么强烈推荐将文本数据文件转换为 Parquet 数据文件,以实现性能和存储收益。当然,转换需要时间,但查询性能的提升在某些情况下可能达到 30 倍或更高,存储的节省可高达 75%!
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
val df = sqlContext.read.load("data\\users.parquet")
//val df = sqlContext.parquetFile("data\\users.parquet")
// val df = sqlContext.sql("SELECT * FROM parquet.`E:\\wgz\\hadoop\\Spark\\SparkLearning\\data\\users.parquet`")
df.show()
JSON
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
val df = sqlContext.read.json("data\\people.json")
df.show()
// +----+-------+
// | age| name|
// +----+-------+
// |null|Michael|
// | 30| Andy|
// | 19| Justin|
// +----+-------+
Hive表
// sc is an existing SparkContext.
val sqlContext = new org.apache.spark.sql.hive.HiveContext(sc)
sqlContext.sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)")
sqlContext.sql("LOAD DATA LOCAL INPATH 'examples/src/main/resources/kv1.txt' INTO TABLE src")
// Queries are expressed in HiveQL
sqlContext.sql("FROM src SELECT key, value").collect().foreach(println)
性能优化
在内存中缓存数据
- sqlContext.cacheTable(“tableName”)
- dataFrame.cache()
- sqlContext.uncacheTable(“tableName”)
Spark SQL可以通过调用sqlContext.cacheTable(“tableName”)或使用内存中的列格式缓存表dataFrame.cache()。然后,Spark SQL将只扫描所需的列,并自动调整压缩以最大限度地减少内存使用和GC压力。你可以调用sqlContext.uncacheTable(“tableName”)从内存中删除表。
SQLContext.setConf 的配置选项
属性名称 | 默认 | 含义 |
---|---|---|
spark.sql.inMemoryColumnarStorage.compressed | True | 设置为true时,Spark SQL将根据数据的统计信息自动为每列选择压缩编解码器。 |
spark.sql.inMemoryColumnarStorage.batchSize | 10000 | 控制列式高速缓存的批量大小。较大的批量大小可以提高内存利用率和压缩率,但是在缓存数据时会面临OOM风险。 |
spark.sql.tungsten.enabled | True | 当为true时,特定查询中的表达式求值的代码将会在运行时动态生成。对于一些拥有复杂表达式的查询,此选项可导致显著速度提升。然而,对于简单的查询,这个选项会减慢查询的执行 |
spark.sql.shuffle.partitions | 200 | 配置shuffer的分区数 |
运行Spark SQL CLI
Spark SQL CLI是一种方便的工具,可以在本地模式下运行Hive Metastore服务,并从命令行执行查询输入。请注意,Spark SQL CLI无法与Thrift JDBC服务器通信。使用方法和原生的hive语句一样
要启动Spark SQL CLI,请在Spark目录中运行以下命令:
./bin/spark-sql
前提是需要hive-site.xml,core-site.xml和hdfs-site.xml拷贝到conf/下