Spark SQL
Spark SQL是构建在Spark RDD之上的一款ETL(Extract Transformation Load)工具,这类似于构建在MapReduce之上的1.x版本的Hive。同Spark RDD的不同之处在于Spark SQL的API可以给Spark计算引擎提供更多的信息(计算数据结构、转换算子),Spark计算引擎可以根据Spark SQL提供的信息优化底层计算任务。目前为止,Spark SQL提供了两种风格的交互式API:Dataset API
/SQL脚本
。
Dataset API
:加强版的RDD操作,例如支持map、flatMap、filter等算子,同时还支持select、where、groupBy、cube等SQL语句,使用者在操作数据的时候必须知道所操作数据的类型,因此通常将这种API操作称为strong-type操作。
val wordPairRDD = spark.sparkContext.textFile("file:///D:/demo/words")
.flatMap(_.split("\\s+"))
.map((_, 1))
//将RDD变成Dataset
wordPairRDD.toDS()
.groupByKey(pair=>pair._1)
.agg(typed.sum[(String,Int)](t=>t._2).name("total"))
.show()
SQL脚本
:使用者无需关注底层 Dataset API
,所有的操作类型都是基于命名列类型
,在操作数据的时候不必关心所操作数据的类型,因此通常将这种操作称为untyped操作
。
word num
a 1
b 1
a 1
select sum(num) from t_words groupBy word
参考:http://spark.apache.org/docs/latest/sql-programming-guide.html
Spark SQL研究的主要对象是Dataset
/Dataframe
(加强版本的RDD)。Dataset是一个分布式的数据集合,在Spark 1.6版本中提供一个新的接口,Dataset提供RDD的优势(强类型,使用强大的lambda函数)以及具备了Spark SQL执行引擎的优点。Dataset可以通过JVM对象构建,然后可以使用转换函数(例如:map、flatMap、filter等算子),目前Dataset API对Scala和Java语言支持比较友好,对Python语言的支持还不算完备。
DataFrame是命名列的数据集-特殊的Dataset,它在概念上等价于关系型数据库,DataFrame可以从很多地方来构建,比如结构化数据文件(json、csv等)、hive中的表(遗留系统对接)或者外部数据库,使用Dataset[Row]的数据集,可以理解DataFrame就是一个Dataset[Row]。
快速入门
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>2.4.3</version>
</dependency>
SQL脚本
//1.创建sparksession对象
val spark = SparkSession.builder().appName("word count")
.master("local[6]")
.getOrCreate()
//2.导入常见的隐式转换|增强 将RDD转换为Dataframe/Dataset
import spark.implicits._
spark.sparkContext.setLogLevel("FATAL") //关闭日志信息
//创建RDD
val wordPairRDD = spark.sparkContext.textFile("file:///D:/demo/words")
.flatMap(_.split("\\s+"))
.map((_, 1))
//将RDD转换为Dataframe
val wordDataframe = wordPairRDD.toDF("word", "num")
//将wordDataframe注册为一个t_words表
wordDataframe.createOrReplaceTempView("t_words")
//写SQL脚本查询t_words
spark.sql("select word,sum(num) total from t_words group by word order by total desc")
.show()
//3.关闭sparksession对象
spark.stop()//close
命名列-API
//1.创建sparksession对象
val spark = SparkSession.builder().appName("word count")
.master("local[6]")
.getOrCreate()
//2.导入常见的隐式转换|增强 将RDD转换为Dataframe
import spark.implicits._
spark.sparkContext.setLogLevel("FATAL") //关闭日志信息
//创建RDD
val wordPairRDD = spark.sparkContext.textFile("file:///D:/demo/words")
.flatMap(_.split("\\s+"))
.map((_, 1))
//将RDD变成Dataframe
import org.apache.spark.sql.functions._
wordPairRDD.toDF("word","num")
.groupBy($"word")
.agg(sum($"num") as "total")
.orderBy($"total" desc)
.show()
//3.关闭sparksession对象
spark.stop()//close
强类型-typed
-不推荐
//1.创建sparksession对象
val spark = SparkSession.builder().appName("word count")
.master("local[6]")
.getOrCreate()
//2.导入常见的隐式转换|增强 将RDD转换为Dataframe
import spark.implicits._
spark.sparkContext.setLogLevel("FATAL") //关闭日志信息
//创建RDD
val wordPairRDD = spark.sparkContext.textFile("file:///D:/demo/words")
.flatMap(_.split("\\s+"))
.map((_, 1))
//将RDD变成Dataset
wordPairRDD.toDS()
.groupByKey(pair=>pair._1)
.agg(typed.sum[(String,Int)](t=>t._2).name("total"))
.rdd
.sortBy(_._2,false,3)
.toDF("word","total")
.show()
//3.关闭sparksession对象
spark.stop()//close
Dataset和DataFrame构建
Dataset√
Dataset是一个特殊的RDD(增强版本的RDD),与RDD的不同在于Spark SQL自己维护了一套序列化和反序列化规范,规避在计算过程中因为多次序列化对计算节点的性能损耗,由于Spark SQL提倡untyped的操作,使用者无需关注所操作数据的类型,只需提供针对命名列操作的算子即可,从而提升计算节点的计算性能。
rdd
> sc.textFile("hdfs://...").flatMap(_.split("\\s+")).map((_,1)).reduceByKey(_+_)
dataset
> val wordPairRDD = spark.sparkContext.textFile("file:///D:/demo/words")
.flatMap(_.split("\\s+"))
.map((_, 1))
> wordPairRDD.toDF("word","num") //无需知道所操作数据类型,只需提供列名
.groupBy($"word")
.agg(sum($"num") as "total")
.orderBy($"total" desc)
.show()
- case-class
case class Person(id:Int,name:String,age:Int,dept:Int)
List(Person(1,"zs",18,1),Person(2,"ls",18,1)).toDS().show()
+---+----+---+----+
| id|name|age|dept|
+---+----+---+----+
| 1| zs| 18| 1|
| 2| ls| 18| 1|
+---+----+---+----+
- tuple元组
List((1,"zs",18,1),(2,"ls",18,1)).toDS().show()
+---+---+---+---+
| _1| _2| _3| _4|
+---+---+---+---+
| 1| zs| 18| 1|
| 2| ls| 18| 1|
+---+---+---+---+
- json数据
case class User(id:BigInt,name:String,age:Long,dept:Long)
spark.read.json("file:///D:/demo/json").as[User]
.show()
- rdd(略)
RDD中的元素必须是
元组
或者是case-class
才可以直接通过toDS()方法创建
Data Frame √
DataFrame是命名列
的数据集,它在概念上等价于关系型数据库。DataFrame可以从很多地方来构建,比如说结构化数据文件、hive中的表或者外部数据库,使用Dataset[row]的数据集,可以理解DataFrame就是一个Dataset[Row]。
- case-class
List(Person(1,"zs",18,1),Person(2,"ls",18,1)).toDF().show()
- tuple元组
List((1,"zs",18,1),(2,"ls",18,1)).toDF("id","name","age","dept").show()
- json数据
spark.read.json("file:///D:/demo/json")
.show()
-
RDD 创建
- RDD[Row]类型
val rowRDD:RDD[Row] = spark.sparkContext.makeRDD(List((1, "zs", 18, 1), (2, "ls", 18, 1))) .map(t => Row(t._1, t._2, t._3, t._4)) val schema = new StructType() .add("id",IntegerType) .add("name",StringType) .add("age",IntegerType) .add("dept",IntegerType) spark.createDataFrame(rowRDD,schema) .show()
- RDD[Javabean]
public class Student implements Serializable { private Integer id; private String name; private Integer age; private Integer dept; public Student()