什么是Spark SQL
sparkSQL是spark提供的用来处理结构化数据的一个模块,他提供了两个编程抽象DateFrame和DateSet。
什么是DataFrame
与RDD类似,不过DateFrame中除了存储数据还存储了结构化的信息如图:
与Hive类似,DateFrame也提供了兴struct、array和map,并且DateFrame在底层会对我们的sql进行优化所以其实际上执行效率是要高于RDD的。
什么是DataSet
1)是Dataframe API的一个扩展,是Spark最新的数据抽象。
2)用户友好的API风格,既具有类型安全检查也具有Dataframe的查询优化特性。
3)Dataset支持编解码器,当需要访问非堆上的数据时可以避免反序列化整个对象,提高了效率。
4)样例类被用来在Dataset中定义数据的结构信息,样例类中每个属性的名称直接映射到DataSet中的字段名称。
5) Dataframe是Dataset的特列,DataFrame=Dataset[Row] ,所以可以通过as方法将Dataframe转换为Dataset。Row是一个类型,跟Car、Person这些的类型一样,所有的表结构信息我都用Row来表示。
6)DataSet是强类型的。比如可以有Dataset[Car],Dataset[Person].
7)DataFrame只是知道字段,但是不知道字段的类型,所以在执行这些操作的时候是没办法在编译的时候检查是否类型失败的,比如你可以对一个String进行减法操作,在执行的时候才报错,而DataSet不仅仅知道字段,而且知道字段类型,所以有更严格的错误检查。就跟JSON对象和类对象之间的类比。
SparkSQL编程
SparkSession新的起始点
在老的版本中,SparkSQL提供两种SQL查询起始点:一个叫SQLContext,用于Spark自己提供的SQL查询;一个叫HiveContext,用于连接Hive的查询。
SparkSession是Spark最新的SQL查询起始点,实质上是SQLContext和HiveContext的组合,所以在SQLContext和HiveContext上可用的API在SparkSession上同样是可以使用的。SparkSession内部封装了sparkContext,所以计算实际上是由sparkContext完成的。
DataFrame
创建:
sparkSession是创建DateFrame的入口。我们创建DateFrame有三种方式:1、从数据源进行创建。2、将其他的RDD进行转换。3、可以从HiveTable进行查询返回。
- 从spark数据源进行创建:
我们可以通过:
spark.read.json/csv/jdbc...(path)来从数据源获取DF
例如:
spark.read.json("/opt/module/spark/examples/src/main/resources/people.json")
- RDD转换为DateFrame:
首先我们需要导入隐式转换: import spark.implicits._注意spark并不是包名而是sparkSession对象的名称。
import spark.implicits._
val peopleRDD = sc.textFile("examples/src/main/resources/people.txt")
peopleRDD: org.apache.spark.rdd.RDD[String] = examples/src/main/resources/people.txt MapPartitionsRDD[3] at textFile
a、手动导入:
peopleRDD.map{
x=>
val para = x.split(",");
(para(0),para(1).trim.toInt)//这块需要将我们组装好的数据和外边的toDF中的列名相对应
}.toDF("name","age")
b、通过反射确定:(样例类)
首先创建一个样例类:
case class Person(name:String,age:Int)
根据样例类将RDD装缓存DF:
peopleRDD.map{
x => val para = x.split(",");People(para(0),para(1).trim.toInt)}.toDF
c、通过编程的方式(了解)
导入所需的类型
import org.apache.spark.sql.types._
import org.apache.spark.sql.types._
创建Schema
val structType: StructType = StructType(StructField("name", StringType) :: StructField("age", IntegerType) :: Nil)
structType: org.apache.spark.sql.types.StructType = StructType(StructField(name,StringType,true), StructField(age,IntegerType,true))
导入所需的类型
import org.apache.spark.sql.Row
import org.apache.spark.sql.Row
根据给定的类型创建二元组RDD
val data = peopleRDD.map{
x => val para = x.split(",");Row(para(0),para(1).trim.toInt)}
data: org.apache.spark.rdd.RDD[org.apache.spark.sql.Row] = MapPartitionsRDD[6] at map at <console>:33
根据数据及给定的schema创建DataFrame
val dataFrame = spark.createDataFrame(data, structType)
dataFrame: org.apache.spark.sql.DataFrame = [name: string, age: int]
DateFrame转换为RDD:
直接调用rdd函数即可:
object Text {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("deom").setMaster("local[*]")
val sc=new SparkContext(conf)
val s=SparkSession.builder().config(conf).getOrCreate();
import s.implicits._
val orgRdd = sc.makeRDD(Array("doudou,15","gg,25"))
val frame = orgRdd.map{
x=>
val param = x.split(",")
person(param(0),param(1))
}.toDF
val rdd = frame.rdd
rdd.foreach(println)
}
}
case class person(name:String,age:String)
输出结果为:
[gg,25]
[doudou,15]
DateSet
dateSet是强类型的数据集合,所以需要提供对应的类型信息。
创建:
case class Person(name: String, age: Long)
val caseClassDS = Seq(Person("Andy", 32)).toDS()
caseClassDS: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]
RDD转换为DataSet
object Text {
def main(args: Array