說明
本博客每週五更新 Spark Sql模塊用於處理結構化數據,結構化數據指DataFrame數據。 Spark sql是從shark發展而來,shark則是爲了兼容Hive數據庫,實現sql任務。
資料
- Spark Sql官網地址
優勢
- 簡化數據操作,Spark Sql支持在DataFrame基礎上實現sql語句操作,降低了數據操作的技術門檻,可以讓產品經理或數據分析工作者,直接高效從數據中提取價值信息。
- 靈活性強,支持自定義UDF
- 支持多種類型數據加載和存儲,文件格式如:json、parquet、jdbc、orc、libsvm、csv、text,提供jdbc數據源,並且支持自定義數據源。
運行方式
Spark Sql支持用戶提交SQL文件,提供三種方式編寫SQL:
- Spark 代碼
- Spark-Sql shell方式,類似spark-submite可設置部署模式和資源,可使用Spark-sql –help 查看配置參數說明
- thriftserver方式, thriftserver jdbc/odbc的實現類似於hive1.2.1的hiveserver2,可以使用spark的beeline命令來測試jdbc server。
優化器及執行計劃
整體流程
總體執行流程如上,從提供的輸入API(SQL,Dataset, dataframe)開始,依次經過unresolved邏輯計劃,解析的邏輯計劃,優化的邏輯計劃,物理計劃,然後根據cost based優化,選取一條物理計劃進行執行。 流程簡化可分爲4步
- analysis:Spark 2.0 以後語法樹生成使用的是antlr4,之前是scalaparse。
- logical optimization:常量合併,謂詞下推,列裁剪,boolean表達式簡化,和其它的規則
- physical planning:物理規劃
- Codegen:codegen技術是用scala的字符串插值特性生成源碼,然後使用Janino,編譯成java字節碼。
自定義優化器
- 實現,繼承Rule[LogicalPlan]
- 註冊:
spark.experimental.extraOptimizations= Seq(MultiplyOptimizationRule)
- 使用:
selectExpr("amountPaid* 1")
自定義執行計劃
主要是實現重載count函數的功能
- 物理計劃:繼承SparkLan實現doExecute方法
- 邏輯計劃:繼承SparkStrategy實現apply
- 註冊到Spark執行策略:
spark.experimental.extraStrategies =Seq(countStrategy)
- 使用:
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年許願每週更新一篇博客,年初堅持到現在只剩一週,給自己鼓掌,加油。