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 是字符串类型,下面语句也只有运行时才进行类型检查。

三者的共性:

  1. RDD,DataFrame,Dataset全都是spark平台下的分布式弹性数据集,为处理超大型数据提供遍历
  2. 三者都有惰性机制,在进行Transformation时不会执行,只有在遇到Action 如foreach时,三者才会开始遍历运算
  3. 三者都会根据spark的内存情况自动缓存运算,不需要担心内存溢出
  4. 三者都有partition,和许多共同函数,如filter,排序等
  5. 对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")

  }
}