spark(八):SparkSql

说明

本博客每周五更新 Spark Sql模块用于处理结构化数据,结构化数据指DataFrame数据。 Spark sql是从shark发展而来,shark则是为了兼容Hive数据库,实现sql任务。

资料

优势

  • 简化数据操作,Spark Sql支持在DataFrame基础上实现sql语句操作,降低了数据操作的技术门槛,可以让产品经理或数据分析工作者,直接高效从数据中提取价值信息。
  • 灵活性强,支持自定义UDF
  • 支持多种类型数据加载和存储,文件格式如:json、parquet、jdbc、orc、libsvm、csv、text,提供jdbc数据源,并且支持自定义数据源。

运行方式

Spark Sql支持用户提交SQL文件,提供三种方式编写SQL:

  1. Spark 代码
  2. Spark-Sql shell方式,类似spark-submite可设置部署模式和资源,可使用Spark-sql –help 查看配置参数说明
  3. thriftserver方式, thriftserver jdbc/odbc的实现类似于hive1.2.1的hiveserver2,可以使用spark的beeline命令来测试jdbc server。

优化器及执行计划

整体流程

总体执行流程如上,从提供的输入API(SQL,Dataset, dataframe)开始,依次经过unresolved逻辑计划,解析的逻辑计划,优化的逻辑计划,物理计划,然后根据cost based优化,选取一条物理计划进行执行。 流程简化可分为4步

  1. analysis:Spark 2.0 以后语法树生成使用的是antlr4,之前是scalaparse。
  2. logical optimization:常量合并,谓词下推,列裁剪,boolean表达式简化,和其它的规则
  3. physical planning:物理规划
  4. Codegen:codegen技术是用scala的字符串插值特性生成源码,然后使用Janino,编译成java字节码。

自定义优化器

  1. 实现,继承Rule[LogicalPlan]
  2. 注册:spark.experimental.extraOptimizations= Seq(MultiplyOptimizationRule)
  3. 使用:selectExpr("amountPaid* 1")

自定义执行计划

主要是实现重载count函数的功能

  1. 物理计划:继承SparkLan实现doExecute方法
  2. 逻辑计划:继承SparkStrategy实现apply
  3. 注册到Spark执行策略:spark.experimental.extraStrategies =Seq(countStrategy)
  4. 使用:spark.sql("select count(*) fromtest")

代码实例

scala语言三种Spark Sql操作实例

指定Schema格式

import org.apache.spark.sql.{Row, SQLContext, SaveMode, SparkSession}
import org.apache.spark.sql.types._
import org.apache.spark.{SparkConf, SparkContext}

object SpecifyingSchema {
  def main(args: Array[String]) {
      //创建Spark Session对象
    val spark = SparkSession.builder().master("local").appName("UnderstandingSparkSession").getOrCreate()

    //从指定的地址创建RDD
    val personRDD = spark.sparkContext.textFile("D:\\temp\\student.txt").map(_.split(" "))

    //通过StructType直接指定每个字段的schema
    val schema = StructType(
      List(
        StructField("id", IntegerType, true),
        StructField("name", StringType, true),
        StructField("age", IntegerType, true)
      )
    )
    //将RDD映射到rowRDD
    val rowRDD = personRDD.map(p => Row(p(0).toInt, p(1).trim, p(2).toInt))

    //将schema信息应用到rowRDD上
    val personDataFrame = spark.createDataFrame(rowRDD, schema)

    //注册表
    personDataFrame.createOrReplaceTempView("t_person")

    //执行SQL
    val df = spark.sql("select * from t_person order by age desc limit 4")

    //显示结果
    df.show()

    //停止Spark Context
    spark.stop()
  }
}

case class方式

import org.apache.spark.sql.SparkSession

//使用case class
object Demo2 {

  def main(args: Array[String]): Unit = {
    //创建SparkSession
    val spark = SparkSession.builder().master("local").appName("My Demo 1").getOrCreate()

    //从指定的文件中读取数据,生成对应的RDD
    val lineRDD = spark.sparkContext.textFile("d:\\temp\\student.txt").map(_.split(" "))

    //将RDD和case class 关联
    val studentRDD = lineRDD.map( x => Student(x(0).toInt,x(1),x(2).toInt))

    //生成 DataFrame,通过RDD 生成DF,导入隐式转换
    import spark.sqlContext.implicits._
    val studentDF = studentRDD.toDF

    //注册表 视图
    studentDF.createOrReplaceTempView("student")

    //执行SQL
    spark.sql("select * from student").show()

    spark.stop()
  }
}

//case class 一定放在外面
case class Student(stuID:Int,stuName:String,stuAge:Int)

结果数据保存到数据库

import java.util.Properties

import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.sql.types.{IntegerType, StringType, StructField, StructType}


//作用:读取本地一个文件, 生成对应 DataFrame,注册表
object Demo1 {

  def main(args: Array[String]): Unit = {
    //创建SparkSession
    val spark = SparkSession.builder().master("local").appName("My Demo 1").getOrCreate()

    //从指定的文件中读取数据,生成对应的RDD
    val personRDD = spark.sparkContext.textFile("d:\\temp\\student.txt").map(_.split(" "))

    //创建schema ,通过StructType
    val schema = StructType(
        List(
          StructField("personID",IntegerType,true),
          StructField("personName",StringType,true),
          StructField("personAge",IntegerType,true)
        )
    )

    //将RDD映射到Row RDD 行的数据上
    val rowRDD = personRDD.map(p => Row(p(0).toInt,p(1).trim,p(2).toInt))

    //生成DataFrame
    val personDF = spark.createDataFrame(rowRDD,schema)

    //将DF注册成表
    personDF.createOrReplaceTempView("myperson")

    //执行SQL
    val result = spark.sql("select * from myperson")

    //显示
    //result.show()

    //将结果保存到oracle中
    val props = new Properties()
    props.setProperty("user","scott")
    props.setProperty("password","tiger")

    result.write.jdbc("jdbc:oracle:thin:@192.168.88.101:1521/orcl.example.com","scott.myperson",props)

    //如果表已经存在,append的方式数据
    //result.write.mode("append").jdbc("jdbc:oracle:thin:@192.168.88.101:1521/orcl.example.com","scott.myperson",props)


    //停止spark context
    spark.stop()
  }
}

总结

Spark Sql是spark实现易用性的重要构成部分,加强了非专业开发者业务处理能力,也增强了功能开发的灵活性。 马上元旦,20年许愿每周更新一篇博客,年初坚持到现在只剩一周,给自己鼓掌,加油。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章