1.简介:
SparkSQL的前身是Shark,Shark的底层实现是依赖于Hive,Shark的发展受制于Hive的发展,后来项目组将Shark项目废弃,保留了其中的一些非常优秀的特点:比如内存列存储技术,动态字节码技术等等,重新组织了一个项目,这个项目就是SparkSQL,同时在hive里面也推出了一个子模块,是hive的计算引擎基于spark,hive-on-spark
需要明确的是,sparkCore中的编程模型是RDD,在SparkSQL中的编程模型是DataFrame,在spark1.6之后又做了进一步的升级Dataset。我们把RDD称之为Spark的第一代编程模型,DataFrame成为第二代编程,Dataset则是第三代编程模型。
官网介绍:Spark SQL**是Apache Spark用于处理结构化数据的模块
特点:1.可集成:可以将SQL查询与Spark程序无缝混合
2.统一数据访问:以相同的方式连接到任何数据源。
3.Hive集成:在现有仓库上运行SQL或HiveQL查询
4.标准连接:通过JDBC和ODBC连接,想当于一个服务器
1.2 编程模型:DataFrame和Dataset和RDD
RDD :
- 仅表示数据集,RDD 没有元数据,也就是说没有字段语义定义
DataFrame:
- 由于 RDD 的局限性,Spark 产生了 DataFrame,DataFrame=RDD+Schema,Schema 是就是元数据,是语义描述信息。
- DataFrame 是一种特殊类型的 Dataset,DataSet[Row] = DataFrame
- DataFrame 自带优化器 Catalyst,可以自动优化程序
- DataFrame 提供了一整套的 Data Source API
DataSetRow :运行时类型检查,比如 salary 是字符串类型,下面语句也只有运行时才进行类型检查。
三者的共性:
- RDD,DataFrame,Dataset全都是spark平台下的分布式弹性数据集,为处理超大型数据提供遍历
- 三者都有惰性机制,在进行Transformation时不会执行,只有在遇到Action 如foreach时,三者才会开始遍历运算
- 三者都会根据spark的内存情况自动缓存运算,不需要担心内存溢出
- 三者都有partition,和许多共同函数,如filter,排序等
- 对DataFrame和Dataset进行操作,需要操作都需要这个包进行支持
import spark.implicits._
2.SparkSession的创建和初始化
简介: Spark中编程的入口,就是各种各样的Context,在SparkSQL也不例外,Spark1.6以前,SparkSQL唯一的入口就是SQLContext,及其子类HiveContext,在Spark1.6之后,便又提供了一个SQLContext和HiveContext的集成者——SparkSession。此时SparkSession就成为了SparkSQL的入口,但是他们的底层都是依赖SparkContext。
2.1 依赖:https://mvnrepository.com/artifact/org.apache.spark/spark-hive
2.2 SparkSession的构建
val spark = SparkSession.builder()//构建一个SaprkSession
.master("local[*]")
.appName("SparkSQLOps")
.getOrCreate()
//早期的SQLContext和HiveContext的构建
//val sqlContext = new SQLContext(SparkContext)
//val hiveContext = new HiveContext(SparkContext)
2.3编程
import org.apache.spark.sql.{Column, DataFrame, SparkSession}
/*
在sparksql生态栈中,专门用于处理结构化数据的
入口:SparkSession
编程模型:DataFrame或者Dataset
*/
object _01SparkSQLps {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder()
.master("local[*]")
.appName("SparkSQLOps")
.getOrCreate()
val peopledf:DataFrame = spark.read.json("file:///E:\\spark\\sql\\people.json")
//是对应与一张表中的某一个字段/列(列名,类型,是否为null)
println("-------------schema约束信息,元数据信息")
peopledf.printSchema()
println("--------------表的内容--------------------")
//查看当前DataFrame二维表中的数据 select * from xxx limit 20
peopledf.show()
println("---------------基本操作-查询age和name-----------------")
peopledf.select("name","age").show()
println("---------------基本操作age > 19的人-----------------")
peopledf.select(new Column("name"),new Column("age"),new Column("height"))
.where("age>19").show()
println("---------------基本操作身高 + 10的人-----------------")
peopledf.select(new Column("name"),new Column("age"),new Column("height").+(10)
.as("height")).show()
println("---------------基本操作分组统计-----------------")
//按照age分组,求出每个组的人数
peopledf.select(new Column("name"),new Column("age"),new Column("height"))
.groupBy("age").count().show()
//按照age分组,求出每个组的人数 并且排序
import spark.implicits._
peopledf.select("age").groupBy("age").count().sort($"count".desc).show()
println("---------------基本操作基于SQL的操作-----------------")
/*
第一步:首先构建一张二维表
createTempView ----创建一个是视图
createOrReplaceTempView ---创建或替换/覆盖一个视图
无Global:只在当前SparkSession范围内有效
有Global:只在当前SparkApplication范围内有效,可以跨越多个SparkSession
*/
peopledf.createTempView("people")
val sql =
"""
| select
| age,
| count(1) as countz
| from people
| group by age
| order by countz desc
| limit 3
""".stripMargin
spark.sql(sql).show()
spark.stop()
}
}
3.SparkSQL的编程模型的创建
3.1DataFrame:就是一张二维表的数据
3.2Javabean+反射-------------不显示结果:需要自定义java类Person
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Row, SparkSession}
import scala.collection.JavaConversions
object Per{
def main(args: Array[String]): Unit = {
val spark = SparkSession
.builder()
.appName("SparkSQLDataFrameOps")
.master("local[*]")
.getOrCreate()
val pList = List(
new Person("穆书岳", 19, 180.5),
new Person("周 攀", 18, 178.5),
new Person("任思勉", 23, 180),
new Person("颜 康", 29, 172.5)
)
//使用工具类进行转换JavaConversions
val jList = JavaConversions.seqAsJavaList(pList)
//dataframe的构建----javabean
val pRDD: RDD[Person] = spark.sparkContext.parallelize(pList)
val pDF:DataFrame = spark.createDataFrame(jList,classOf[Person])
val personDF: DataFrame = spark.createDataFrame(pRDD, classOf[Person])
personDF.show()
}
}
3.3通过 case class 创建 DataFrames(反射)
//定义case class,相当于表结构
case class People(var name:String,var age:Int)
object TestDataFrame1 {
def main(args: Array[String]): Unit = {
//创建sparkconf()并设置App名称
val conf = new SparkConf().setAppName("RDDToDataFrame").setMaster("local")
//sqlContext要依赖SaprkContext
val sc = new SparkContext(conf)
//创建SQLContext
val context = new SQLContext(sc)
// 将本地的数据读入 RDD, 并将 RDD 与 case class 关联
val lineRDD = sc.textFile("E:\\666\\people.txt").map(_.split(","))
val peopleRDD = lineRDD.map(x => People(x(0),x(1).toInt))
//这里的split表示切两次,一次是切完放左边,一次切完放右边。和上面效果一样
/* val peopleRDD = sc.textFile("E:\\666\\people.txt")
.map(line => People(line.split(",")(0), line.split(",")(1).trim.toInt))
*/
import context.implicits._
// 将RDD 转换成 DataFrames
val df = peopleRDD.toDF
//将DataFrames创建成一个临时的视图
df.createOrReplaceTempView("people")
//使用SQL语句进行查询
context.sql("select * from people").show()
}
}
3.4动态编程(通过 structType 创建 DataFrames(编程接口))
object StudentSparkSQL2 {
def main(args: Array[String]) {
// 创建 SparkConf() 并设置 App名称 以及master
val conf = new SparkConf().setMaster("local").setAppName("StructType SparkSQL")
//SQLContext要依赖 SparkContext
val sc = new SparkContext(conf)
// 创建 SQLContext
val sqlContext = new SQLContext(sc)
// 从指定的地址创建 RDD
val personRDD = sc.textFile(args(0)).map(_.split(","))
// 通过 StructType直接指定每个字段的 schema
val schema = StructType(
List(
//StructField指定每一个列的列名,类型,和是否为空
StructField("id", IntegerType, true),
StructField("name", StringType, true),
StructField("sex", StringType, true),
StructField("age", IntegerType, true),
StructField("department", StringType, true)
)
)
// 将 RDD映射到 rowRDD// 将 schema信息应用到 rowRDD上
val rowRDD = personRDD.map(p => Row(p(0).toInt, p(1).trim, p(2).trim, p(3).toInt, p(4).trim))
// 注册表 personDataFrame.registerTempTable("t_student")
val personDataFrame = sqlContext.createDataFrame(rowRDD, schema)
// 执行 SQL
val df = sqlContext.sql("select department, count(*) as total from t_student " + "group by department having total > 6 order by total desc")
// 将结果以 JSON的方式存储到指定位置
df.write.json(args(1))
// 停止 Spark Context
sc.stop()
}
}
3.5 Dataset创建
case class Student(name: String, age: Int, height: Double)
object _03SparkSQLDatasetOps {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder()
.master("local[*]")
.appName("dataset")
.getOrCreate()
val list = List(
new Student("穆书岳", 19, 180.5),
new Student("周 攀", 18, 178.5),
new Student("任思勉", 23, 180),
new Student("颜 康", 29, 172.5)
)
import spark.implicits._
val pDataset:Dataset[Student] = spark.createDataset(list)
pDataset.show()
//使集合实现并行化
val rdd = spark.sparkContext.parallelize(list)
spark.createDataset(rdd).show()
spark.stop()
}
}
/*注意:
在构建dataset的时候,必须要给集合提供一个Encoder编码器,将对应的数据类型,转化为StructType元数据信息;
同时,为了能够使用这个Encoder,必须要导入sparksession中的隐式转换spark.implicts._。二者缺一不可,否则异常
*/
3.6 DataFrame和Dataset以及RDD之间的互相转换
需要导入隐式转换 import spark.implicts._
要转化为dataframe,就调用toDF
要转化为dataset,就调用toDS(dataframe无法直接转化为dataset)
要转化为rdd,就调用rdd
也可以这样转换:
1.RDD -> DataFrame :
rdd.map(para => (para(0).trim(),para(1).trim().toInt)).toDF("name","age")
//通过反射来设置
rdd.map(para => Person(para(0),para(1).trim.toInt)).toDF()
//通过编程方式设置Schema,适合编译期不能确定列的情况
schemaString.map(fieldName => StructField(fieldname,StringType,nullable=true))
val schema = StructType(fields)
val rdd[Row] = rdd.map(attributes => Row(attributes(0),attributes(1).trim))
val peopeDF = spark.createDataFrame(rdd[Row],schema)
2.DataFrame ->RDD:
dataFrame.rdd 输出:Array([Michael,29],[Andy,30],[Justin,19])
1.RDD -> DataSet:
rdd.map(para => Person(para(0).trim(),para(1).trim().toInt)).toDS
2.DataSet -> RDD:
dataSet.rdd 输出:Array(Person(Michael,29),Person(Andy,30),Person(Justin,19))
1.DataFrame->DataSet:
dataFrame.to[Person]
2.DataSet -> DataFrame
dataSet.toDF
4.数据源
数据得读取(json,parquet格式)
object TestRead {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("TestDataFrame2").setMaster("local")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
//方式一
val df1 = sqlContext.read.json("E:\\666\\people.json")
val df2 = sqlContext.read.parquet("E:\\666\\users.parquet")
//方式二
val df3 = sqlContext.read.format("json").load("E:\\666\\people.json")
val df4 = sqlContext.read.format("parquet").load("E:\\666\\users.parquet")
//方式三,默认是parquet格式
val df5 = sqlContext.load("E:\\666\\users.parquet")
}
}
数据的保存
object TestSave {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("TestDataFrame2").setMaster("local")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
val df1 = sqlContext.read.json("E:\\666\\people.json")
//方式一
df1.write.json("E:\\111")
df1.write.parquet("E:\\222")
//方式二
df1.write.format("json").save("E:\\333")
df1.write.format("parquet").save("E:\\444")
//方式三
df1.write.save("E:\\555")
}
}