spark-sql

sparkSql

        使用sql来进行操作,简化rdd的开发

        DataFrame是一种以rdd为基础的分布式数据集,也就类似于二维表格,只关心数据的含义,提供详细的结构信息

        DataSet是分布式数据集合,,是DataFrame的一个扩展

        sparkcore中的上下文环境对象是sparkContext,sparksql中的上下文就用的sparksession

简单演示

新建一个user.json

spark读取的json要求每一行是json格式

打开spark-shell.cmd

val df = spark.read.json("user.json")

df.show //查看数据

df.createTempView("user") //创建临时视图

spark.sql("select * from user where age=18").show

idea创建sparksql环境对象

  def main(args: Array[String]): Unit = {
    //创建sparkSql运行环境
    val sparkSql: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSql")
    val session = SparkSession.builder().config(sparkSql).getOrCreate()
    session.stop()
  }

DataFrame基本操作

  def main(args: Array[String]): Unit = {
    //创建sparkSql运行环境
    val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sql")
    // spark 就是上下文环境
    val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
    //读取当前idea工程下的data下的user.json
    val df: DataFrame = spark.read.json("data/user.json")
    // 创建一个当前环境只能有一个的视图
    df.createOrReplaceTempView("user")
    // 通过sql进行查询
    spark.sql("select name,age+1 from user").show()
    // 通过DSL进行查询
    //需要使用转化操作的时候需要引入上下文下的implicits
    import spark.implicits._
    df.select("name","age").show()
    df.select('age + 1).show()
    // dataSet
    // 本质dataframe就是一个dataset[row]
    val data = Seq(1,2,3,4)
    data.toDS().show()
    // rdd <=>DataFrame
    val value: RDD[(Int, String)] = spark.sparkContext.makeRDD(List((1, "zhangsan"), (2, "lisi")))
    val d: DataFrame = value.toDF("id", "name")
    d.rdd
    // DataFrame <=> DataSet
    val ds: Dataset[User] = d.as[User]
    ds.toDF().show()
    // rdd <=> DataSet
    val ds1: Dataset[User] = value.map {
      case (a, b) => {
        User(a, b)
      }
    }.toDS() // 包装满足ds的条件然后直接转换
    ds1.rdd // 转换回rdd
    spark.close()
  }
  case class User(id:Int,name:String)

创建视图(了解)

临时表

// 创建视图有可能重复
df.createTempView

// 全局创建有可能重复
df.createGlobalTempView

// 创建视图防止有多个
df.createOrReplaceTempView

// 全局创建防止有多个
df.createOrReplaceGlobalTempView

// 创建全局在查询的时候需要全路径访问要加上global_temp.
// 且不能修改数据只能查询

DSL语法

DataFrame提供一个特定语言区管理结构化数据,可以在scala,java,python和r中使用DSL,使用DSL语法风格就不必创建临时视图了

rdd和dataframe和dataset相互转化

dataframe和dataset进行很多操作都需要导入这个,是当前上下文的对象spark

import spark.implicits._

DataFrame其实是特定泛型的dataSet

dataframe转换dataset就差一个业务类型class 

// 输出结构
df.printSchema

// 查询age列
df.select("age").show

// 查看年龄+1
import session.implicits._ //是当前上下文变量的
df.select($"age"+1).show //需要加上$或者使用'
df.select('age +1).show

// 条件过滤
df.filter('age > 17).show

// rdd转换dataframe
val rdd = sc.makeRDD(List(1,2,3,4))
val df = rdd.toDF("id")

// dataframe转换rdd
val rdd = df.rdd

DataSet是具有强类型的数据集合,需要提供对应的类型信息

// 创建dataset
case class Person(name:String,age:Long)//创建类测试用
val list = List(Person("aa",12),Person("bb",12))
list.toDS //结果:res20: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]
list.toDS.show

// dataframe转换dataset
df// res23: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
case class Emp(age:Long,name:String) //创建指定类型的对象
df.as[Emp] //加上类型:结果=org.apache.spark.sql.Dataset[Emp] = [age: bigint, name: string]

//dataset转换dataframe
df.toDF

//dataset和rdd相互转换
rdd.toDS //rdd转换dataset
ds.rdd  //dataset转换rdd

UDF函数

用户自定义函数用在使用上下文.sql对列进行函数操作

    val session = SparkSession.builder().config(sparkSql).getOrCreate()
    val df: DataFrame = session.read.json("data/user.json")
    df.createOrReplaceTempView("user")
    session.udf.register("prefixName",(name:String)=>{//进行注册函数
      "Name:"+ name
    })
    session.sql("select age,prefixName(name) from user").show

弱类型函数实现聚合函数

如果我们需要自定义一个聚合函数的话那么就要用到自定义聚合函数类

弱类型容易把数据搞错

  def main(args: Array[String]): Unit = {
    //创建sparkSql运行环境
    val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sql")
    // spark 就是上下文环境
    val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
    val df: DataFrame = spark.read.json("data/user.json")
    df.createOrReplaceTempView("user")
    spark.udf.register("ageAge",new MyAvgUDAF())
    spark.sql("select ageAge(age) from user").show()
    spark.close()
  }
  // 自定义聚合函数类
  class MyAvgUDAF extends UserDefinedAggregateFunction{
    // 输入结构 input 传入数据的结构
    override def inputSchema: StructType = {
      StructType(
        Array(
          StructField("age",LongType)
        )
      )
    }
    // 缓冲区的数据结构 buffer
    override def bufferSchema: StructType = {
      StructType(
        Array(
          StructField("total",LongType),
          StructField("count",LongType)
        )
      )
    }
    // 函数计算结果的数据类型 out
    override def dataType: DataType = LongType
    // 函数的稳定性
    override def deterministic: Boolean = true
    // 缓冲区初始化
    override def initialize(buffer: MutableAggregationBuffer): Unit = {
      buffer(0) = 0L //初始化缓冲区的值等同于 buffer.update(0,0L)
      buffer(1) = 0L
    }
    // 根据输入的值更新缓冲区数据
    override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
      buffer.update(0,buffer.getLong(0) + input.getLong(0))
      buffer.update(1,buffer.getLong(1)+1)
    }
    // 缓冲区合并
    override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
      buffer1.update(0,buffer1.getLong(0) + buffer2.getLong(0))
      buffer1.update(1,buffer1.getLong(1) + buffer2.getLong(1))
    }
    // 计算平均值
    override def evaluate(buffer: Row): Any = {
      buffer.getLong(0)/buffer.getLong(1)
    }
  }

强类型函数实现聚合函数1

版本不对可能会无法使用3.0后才能使用

  def main(args: Array[String]): Unit = {
    //创建sparkSql运行环境
    val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sql")
    // spark 就是上下文环境
    val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
    val df: DataFrame = spark.read.json("data/user.json")
    df.createOrReplaceTempView("user")
    spark.udf.register("ageAge",functions.udaf(new MyAvgUDAF()))
    spark.sql("select ageAge(age) from user").show()
    spark.close()
  }
  // 自定义聚合函数类
  // Aggregator 定义泛型
  // in: 输入的数据类型  buf: 缓冲区的类型    out: 输出的数据类型
  case class Buff(var total:Long,var count:Long)
  class MyAvgUDAF extends Aggregator[Long,Buff,Long]{
    // 缓冲区的初始化
    override def zero: Buff = {
      Buff(0L,0L)
    }
    // 根据输入的数据更新缓冲区的数据
    override def reduce(b: Buff, a: Long): Buff = {
      b.total = b.total + a
      b.count = b.count + 1
      b
    }
    // 合并缓冲区
    override def merge(b1: Buff, b2: Buff): Buff = {
      b1.total = b1.total + b2.total
      b1.count = b1.count + b2.count
      b1
    }
    // 计算结果
    override def finish(reduction: Buff): Long = {
      reduction.total / reduction.count
    }
    // 缓冲区的编码操作
    override def bufferEncoder: Encoder[Buff] = Encoders.product
    // 输出的编码操作
    override def outputEncoder: Encoder[Long] = Encoders.scalaLong
  }

强类型函数实现聚合函数2

早期版本使用方式

  def main(args: Array[String]): Unit = {
    //创建sparkSql运行环境
    val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sql")
    // spark 就是上下文环境
    val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
    val df: DataFrame = spark.read.json("data/user.json")
    import spark.implicits._
    val ds: Dataset[User] = df.as[User]
    // 将udf函数转换成查询的列对象
    val udaf: TypedColumn[User, Long] = new MyAvgUDAF().toColumn
    ds.select(udaf).show()
    spark.close()
  }
  // 自定义聚合函数类
  // Aggregator 定义泛型类
  // in: 输入的数据类型  buf: 缓冲区的类型    out: 输出的数据类型
  case class User(name:String,age:Long)
  case class Buff(var total:Long,var count:Long)
  class MyAvgUDAF extends Aggregator[User,Buff,Long]{
    // 缓冲区的初始化
    override def zero: Buff = {
      Buff(0L,0L)
    }
    // 根据输入的数据更新缓冲区的数据
    override def reduce(b: Buff, a: User): Buff = {
      b.total = b.total + a.age
      b.count = b.count + 1
      b
    }
    // 合并缓冲区
    override def merge(b1: Buff, b2: Buff): Buff = {
      b1.total = b1.total + b2.total
      b1.count = b1.count + b2.count
      b1
    }
    // 计算结果
    override def finish(reduction: Buff): Long = {
      reduction.total / reduction.count
    }
    // 缓冲区的编码操作
    override def bufferEncoder: Encoder[Buff] = Encoders.product
    // 输出的编码操作
    override def outputEncoder: Encoder[Long] = Encoders.scalaLong
  }

数据读取和保存

sparksql提供了通用的保存数据和数据加载的方式,sparksql默认读取和保存的文件格式为parquet

加载数据

spark.read.load  是加载数据的通用方法,默认只能读取链式存储的数据

spark.read.format("json").load("json路径")  // 读取json数据

spark.read.json("json路径") // 直接读取json文件

spark.sql("select * from json.`路径`").show()   // sql进行转换直接读取

json

        sparksql能够自动 推测json格式,但前提是要满足每一行都满足json的格式

csv

        spark.read.format("csv").option("sep", ",").option("inferSchema",true).option("header","true").load("路径")

Mysql

        sparksql可以通过jdbc从关系数据库中读取数据的方式创建DataFrame,通过一系列的计算之后,还可以写会关系型数据库中

idea需要的依赖

        
            mysql
            mysql-connector-java
            5.1.47
        
  def main(args: Array[String]): Unit = {
    val mysql: SparkConf = new SparkConf().setMaster("local[*]").setAppName("mysql")
    val session: SparkSession = SparkSession.builder().config(mysql).getOrCreate()
    //读取mysql数据
    val df: DataFrame = session.read
      .format("jdbc")
      .option("url", "jdbc:mysql://192.168.9.140:3306/contest?")
      .option("driver", "com.mysql.jdbc.Driver")
      .option("user", "root")
      .option("password", "Aa12589+")
      .option("dbtable", "user1")
      .load()
    df.show()
    // 写入mysql数据
    df.write
      .format("jdbc")
      .option("url", "jdbc:mysql://192.168.9.140:3306/contest?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false")
      .option("driver", "com.mysql.jdbc.Driver")
      .option("user", "root")
      .option("password", "Aa12589+")
      .option("dbtable", "user2")
      .mode(SaveMode.Append)
      .save()
    session.close()
  }

数据写出

df.write.sava("路径") // 保存文件以parquet文件格式

df.write.format("json").sava("路径") // 保存json文件

以上的操作保存如果存在是会报错的

df.write.format("json").mode("append").save("output") // append追加, overwrite覆盖,ignore 忽略

spark操作hive

spark内置hive

spark中默认有内置的hive

首先我们打开spark-shell

spark目录下没有什么 

spark.sql("show tables").show() // 会产生一个源存储目录metastore_db

 当我们用json数据创建一个临时视图表

因为我用的是spark on yarn,所以文件需要上传到hdfs

且需要删除虚拟机spark目录下的元数据目录,因为会和hdfs里的冲突

hadoop fs -moveFromLocal ./user.json /data/user.json
var df = spark.read.json("/data/user.json")
df.createOrReplaceTempView("user")
spark.sql("show tables").show()

spark-sql_第1张图片

内置hive创建表插入数据 

创建一个表

spark.sql("create table user1(id int)")

创建一个id.txt,一行一个数据

插入到表中

spark.sql("load data local inpath '/bigdata/spark/id.txt' into table user1")

spark操作外部hive

操作本地hive,把hvie的hive-site.xml复制到spark的conf下,还有把mysql的连接驱动包放到spark下的jar

然后重新启动即可

idea操作hive

首先导入依赖

把hive-site.xml复制到resources下

        
            org.apache.spark
            spark-hive_2.12
            2.4.7
        
        
            org.apache.hive
            hive-exec
            2.3.4
        
  def main(args: Array[String]): Unit = {
    // 使用spark连接外置hive
    val hive: SparkConf = new SparkConf().setMaster("local[*]").setAppName("hive")
    //enableHiveSupport 启用hive支持
    val session: SparkSession = SparkSession.builder().enableHiveSupport().config(hive).getOrCreate()
    //拷贝hive-site.xml到resources下
    //启用hive支持
    
    session.close()
  }

操作连接hivemysql

sbin/start-thriftserver.sh 

spark下的,需要启动hive的server

bin/beeline -u jdbc:hive2://master:10000 -n root

你可能感兴趣的:(spark,scala,开发语言,spark,学习,大数据)