[本文2000字左右,预计阅读需要8-15分钟]
在之前一系列的从零开始学习大数据系列文章中,我们已经学习了很多关于Spark这一组件的基础知识,那么从今天开始我们来学习Spark生态圈中Spark SQL这一重要模块。
什么是Spark SQL
Spark 1.0 推出 Spark SQL,是 Spark 生态系统中最活跃的组件之一。能够利用 Spark 进行结构化的存储和操作。结构化数据可以来自外部源:比如Hive/Json/parquet,
Spark SQL是一个Spark模块用于结构化数据处理。与基本的Spark RDD API不同,Spark SQL提供的接口为Spark提供了有关数据结构和正在执行的计算的更多信息。在内部,Spark SQL使用此额外信息来执行额外的优化。它提供了两个编程抽象分别叫做DataFrame和DataSet,它们用于作为分布式SQL解析引擎。
为什么要学习Spark SQL
在Spark生态圈当中,Spark SQL可以说是非常重要的一个模块,为Spark系统提供交互式查询。而Spark SQL对于Spark系统的重要性,就相当于Hive对Hadoop系统的重要性,Hive是将Hive SQL转换成MapReduce,然后提交到集群上执行的,大大简化了编写MapReduce程序的复杂性,而且MapReduce这种计算模型执行效率比较慢。类比Hive,Spark 是将Spark SQL转换成RDD,然后提交到集群上执行,执行效率非常快!因此Spark SQL是Spark学习当中的重点之一。
Spark SQL的优点
易整合
将sql查询与spark程序无缝混合,可以使用java、scala、python、R等语言的API操作。
统一的数据访问形式
各种各样的数据源,通过同一个API来访问。
eg:read.json() read.jdbc() read.parquet() write.json() write.jdbc()
兼容 Hive
现有仓库上运行未修改的Hive查询。Spark SQL重用了Hive前端和MetaStore,为您提供与现有Hive数据,查询和UDF的完全兼容性。只需将其与Hive一起安装即可。
标准的数据连接
可以使用行业标准的JDBC或ODBC连接
Spark SQL编程接口演变
spark-core RDD SparkContext sparksql DataFrame SQLContextsparksql Dataset SparkSessionspark1.0 RDDspark1.3 DataFramespark1.6 Dataset spark2.0之后,Dataset和DataFrame整合。type DataFrame = Dataset[Row]
DataFrame
DataFrame简介
与RDD类似,但DataFrame更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即schema。同时,与Hive类似,DataFrame也支持嵌套数据类型(struct、array和map)。从API易用性的角度上看,DataFrame API提供的是一套高层的关系操作,比函数式的RDD API要更加友好,门槛更低。由于与R和Pandas的DataFrame类似,Spark DataFrame很好地继承了传统单机数据分析的开发体验。
其实说白了DataFrame也是一个弹性的分布式数据集。只不过是基于RDD的基础之上进行了schema的封装。如下图所示:
创建DataFrame的形式
第一种
利用反射来推断包含特定类型对象的RDD的schema,适用对已知数据结构的RDD转换。
只要有了DataFrame,就可以调用DataFrame上的算子来编程。有2种语法风格的操作。
1,DSL 类似于面向函数式编程。
2,SQL语法风格 写sql语句
scala> val rdd1 = sc.textFile("hdfs://hdp-01:9000/person.txt")rdd1: org.apache.spark.rdd.RDD[String] = hdfs://hdp-01:9000/person.txt MapPartitionsRDD[1] at textFile at :24scala> case class Person(name:String,age:Int,add:String)defined class Personscala> val rdd2 = rdd1.map(str =>str.split(","))rdd2: org.apache.spark.rdd.RDD[Array[String]] = MapPartitionsRDD[2] at map at :26scala> val rdd3 = rdd2.map(t=> Person(t(0),t(1).toInt,t(2)))rdd3: org.apache.spark.rdd.RDD[Person] = MapPartitionsRDD[3] at map at :30scala>scala> val pdf = rdd3.toDF()pdf: org.apache.spark.sql.DataFrame = [name: string, age: int ... 1 more field]scala> pdf.printSchema()root |-- name: string (nullable = true) |-- age: integer (nullable = false) |-- add: string (nullable = true)scala> pdf.select("name")res2: org.apache.spark.sql.DataFrame = [name: string]scala> pdf.select("name").show()+----+|name|+----+| zs|| ls|| ww|| zl|+----+scala> pdf.filter("age >21").show +----+---+---------+|name|age| add|+----+---+---------+| ls| 22|ls.qq.com|| ww| 22|ww.qq.com|+----+---+---------+
编程思路:
1.创建SQLContext
2.读取数据,数据切分
3.创建一个case class
4.把数据封装成RDD[类]
5.调用toDF方法,生成DataFrame
6.利用sql风格(注册临时表,查询) / DSL 风格
第二种
使用编程接口,构造一个schema并将其应用在已知的RDD上。
/ 编程入口val sqlContext = new SQLContext(sc)val file: RDD[String] = sc.textFile("wc.txt")val rdd2: RDD[String] = file.flatMap(_.split(" "))// RDD[String] --> RDD[Row]val rowRDD: RDD[Row] = rdd2.map(Row(_))// createDataFrame(rowRDD: RDD[Row], schema: StructType)// 自定义的schemaval schema =StructType( Array( StructField("word",StringType,true) ))val wdf: DataFrame = sqlContext.createDataFrame(rowRDD,schema)
编程思路:
1.创建SQLContext
2.读取数据,数据切分
3.把数据封装成RDD[Row]
4.自定义schema信息
5.调用createDataFrame(rowRDD,schema),创建DataFrame
6.利用sql风格(注册临时表,查询) / DSL 风格
DataFrame编程(IDEA)
创建一个Maven工程
<dependency> <groupId>org.apache.sparkgroupId> <artifactId>spark-sql_2.11artifactId> <version>${spark.version}version>dependency>
编程实现
val conf = new SparkConf() .setMaster("local[*]") .setAppName(this.getClass.getSimpleName) val sc = new SparkContext(conf) // 编程入口 val sqlContext = new SQLContext(sc) import sqlContext.implicits._ val file: RDD[String] = sc.textFile("person.txt") val personRdd: RDD[Person] = file.map(str => { val split = str.split(",") val name = split(0) val age = split(1).toInt val address = split(2) // 封装数据 Person(name, age, address) }) // 获取到DataFrame val pdf: DataFrame = personRdd.toDF() // 打印schema pdf.printSchema() // 简写