Spark(四) SQL

一、简介

Spark SQL是Spark用来处理结构化数据的一个模块,它提供了2个编程抽象:DataFrame和DataSet,并且作为分布式SQL查询引擎的作用。
之前Hive是将hql转换成MapReduce然后放在集群上执行,简化了编写MapReduce的复杂性,但是由于MapReduce执行的效率比较慢,所以产生了SparkSQL,它是将SQL转换成RDD,然后提交到集群执行,效率就会变快。

二、DataFrame

DataFrame也是一个分布式容器。类似于数据库中的二维表格。但是它又比二维表格多了一些记录数据结构的信息 :schema
DataFrame提供了详细的结构信息,使得Spark SQL可以清楚地知道该数据集中包含哪些列,每列的名称和类型各是什么。DataFrame是为数据提供了Schema的视图。可以把它当做数据库中的一张表来对待,

三、DataSet

  1. 可以结合样例类来使用,样例类被用来在Dataset中定义数据的结构信息,样例类中每个属性的名称直接映射到DataSet中的字段名称。
  2. DataSet是强类型的。比如可以有DataSet[String],DataSet[Person]
  3. DataFrame只是知道字段,但是不知道字段的类型,所以在执行这些操作的时候是没办法在编译的时候检查是否类型失败的,比如你可以对一个String进行减法操作,在执行的时候才报错,而DataSet不仅仅知道字段,而且知道字段类型,所以有更严格的错误检查。就跟JSON对象和类对象之间的类比。
  4. DataFrame和DataSet的关系: Dataframe是Dataset的特列,DataFrame=Dataset[Row] ,所以可以通过as方法将Dataframe转换为Dataset。Row是一个类型,跟Car、Person这些的类型一样,所有的表结构信息我都用Row来表示。

四、Spark SQL编程

4.1两种编程风格

4.1.1 SQL风格

例:

val df: DataFrame = spark.read.json("F:\\datas\\a.json")
df.createOrReplaceTempView("t_user")

val sqlDF: DataFrame = spark.sql("select * from t_user")
sqlDF.show()

4.1.2 DSL风格

例:

import spark.implicits._

val df = spark.read.json("F:\\datas\\a.json")
df.printSchema()//打印Schema结构
//指定列查询
df.select($"name",$"age").show()	 
//查询大于20岁的
df.filter($"age">20).show()
//根据年龄分组,统计个数
df.groupBy("age").count().show()

4.2 DataFrame和DataSet的创建方式

4.2.1 创建DataFrame

数据:

{"name":"zhangsan","age":20}
{"name":"李四","age":22}
{"name":"wanguw","age":24}
val spark: SparkSession =
      SparkSession.builder()
        .appName("ceshi")
        .master("local[*]")
        .getOrCreate()

val df: DataFrame = spark.read.json("F:\\datas\\a.json")
df.show()

spark.stop()

4.2.2 创建DataSet

//定义case class
case class Person(name:String,age:Long)

//创建List集合,转换为DataSet对象

import spark.implicits._
val ds: Dataset[Person] = List(Person("zhangsan",20),Person("lisi",22)).toDS()
ds.show()

4.3 RDD和DataFrame和DataSet之间的转换

4.3.1 RDD转换为DataSet

import spark.implicits._

val rdd1: RDD[String] = spark.sparkContext.textFile("F:\\datas\\a.txt")
rdd1.map(v=>{
      val arr: Array[String] = v.split("\t")
      Person(arr(0),arr(1).toLong)
}).toDS().show()

4.3.2 DataSet转换为RDD

val ds: Dataset[Person] = List(Person("zhangsan",20),Person("lisi",23)).toDS()
val rdd: RDD[Person] = ds.rdd

4.3.3 DataFrame转换为DataSet

import spark.implicits._

val df: DataFrame = spark.read.json("F:\\datas\\a.json")
val ds: Dataset[Person] = df.as[Person]
ds.show()

4.3.4 DataSet转换为DataFrame

import spark.implicits._

val ds: Dataset[Person] = List(Person("zhangsan",20),Person("lisi",23)).toDS()
val df: DataFrame = ds.toDF()
df.show()

五、自定义函数

5.1 自定义单行函数

import spark.implicits._
val df: DataFrame = List(("zhangsan", 20, 1), ("lisi", 20, 1), ("xiaohong", 18, 0)).toDF("name", "age", "sex")
df.createOrReplaceTempView("t_user")
spark.udf.register("convert_sex",(sex:Int)=>{
    sex match {
   	 	case 0 => "女"
    	case 1 => "男"
    }
})
spark.sql("select name,age,convert_sex(sex) sex from t_user").show()

5.1 自定义聚合函数

class CustomSum extends UserDefinedAggregateFunction{

  //函数输入参数的类型
  override def inputSchema: StructType = {
    new StructType().add("input1",IntegerType)
  }

  //缓冲区中值的数据类型
  override def bufferSchema: StructType = {
    new StructType().add("sum",IntegerType)
  }

  //返回值的数据类型
  override def dataType: DataType = IntegerType

  //对于相同的输入是否一直返回相同的输出
  override def deterministic: Boolean = true

  //初始化,
  override def initialize(buffer: MutableAggregationBuffer): Unit = {
    buffer(0) = 0
  }

  //将缓冲区的数据加上输入的数据,然后更新到缓冲区中
  override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
    val d1: Int = buffer.getAs[Int](0)
    val d2: Int = input.getAs[Int](0)
    buffer.update(0,d1+d2)
  }

  //合并缓冲区的数据
  override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
    val d1: Int = buffer1.getAs[Int](0)
    val d2: Int = buffer2.getAs[Int](0)
    buffer1.update(0,d1+d2)
  }

  //返回最终结果
  override def evaluate(buffer: Row): Any = {
    buffer.getAs[Int](0)
  }
}

使用:

val df: DataFrame = List(("zhangsan", 20, 1), ("lisi", 20, 1), ("xiaohong", 18, 0))
      .toDF("name", "age", "sex")

df.createOrReplaceTempView("t_user")
spark.udf.register("customSum",new CustomSum)
spark.sql("select customSum(age) sex from t_user").show()

六、Load/Save

对于Spark SQL的DataFrame来说,无论是从什么数据源创建出来的DataFrame,都有一些共同的load和save操作。
load操作主要用于加载数据,创建出DataFrame
save操作,主要用于将DataFrame中的数据保存到文件中

6.1 parquet

Parquet是一种流行的列式存储格式,可以高效地存储具有嵌套字段的记录。Parquet格式经常在Hadoop生态圈中被使用,它也支持Spark SQL的全部数据类型。Spark SQL 提供了直接读取和存储 Parquet 格式文件的方法。

//保存数据
import spark.implicits._
val df: DataFrame = List((1,"zhangsan",20),(2,"lisi",23),(3,"wangwu",23)).toDF("id","name","age")
df.write.parquet("f:\\datas\\parquet")
    
//读取数据
spark.read.parquet("f:\\datas\\parquet").show()

6.2 JDBC

Spark SQL可以通过JDBC从关系型数据库中读取数据的方式创建DataFrame,通过对DataFrame一系列的计算后,还可以将数据再写回关系型数据库中。

val properties = new Properties()
properties.put("user", "root")
properties.put("password", "123456")
val df: DataFrame = List((1,"zhangsan",20),(2,"lisi",23),(3,"wangwu",23)).toDF("id","name","age")
df.write.jdbc("jdbc:mysql://localhost:3306/test1", "df_user", properties)
spark.read.jdbc("jdbc:mysql://localhost:3306/test1","df_user",properties).show()

七、Spark整合Hive

步骤

  1. 将hive-site.xml copy 到spark/conf/目录下
  2. 把 Mysql 的驱动 copy 到 spark/jars/目录下.
  3. 如果访问不到hdfs, 则需要把core-site.xml和hdfs-site.xml 拷贝到conf/目录下
  4. 启动测试:
spark.sql("use spark")
spark.sql("select * from t_user").show

代码中使用HIVE

  1. 拷贝 hive-site.xml 到 resources 目录下
  2. 在pom文件中添加依赖
<dependency>
       <groupId>org.apache.spark</groupId>
       <artifactId>spark-hive_2.11</artifactId>
       <version>2.4.3</version>
</dependency>

<dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.38</version>
</dependency>
  1. 测试
import org.apache.spark.sql.SparkSession
object HiveDemo {
    def main(args: Array[String]): Unit = {
         val spark: SparkSession = SparkSession
                  .builder()
                  .master("local[*]")
                  .appName("Test")
                  .enableHiveSupport()
                  .getOrCreate()

        import spark.implicits._
        spark.sql("use spark")
        spark.sql("select * from t_user").show
        spark.stop()
    }
}

你可能感兴趣的:(大数据,spark)