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年許願每週更新一篇博客,年初堅持到現在只剩一週,給自己鼓掌,加油。

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