SparkSQL基本介紹
什麼是SparkSQL?
用於處理結構化數據的Spark模塊。
可以通過DataFrame和DataSet處理數據。
SparkSQL特點
1、易整合
可以使用java、scala、python、R等語言的API操作。
2、統一的數據訪問
連接到任何數據源的方式相同。
3、兼容Hive
4、標準的數據連接(JDBC/ODBC)
SQL優缺點
優點:表達非常清晰,難度低、易學習。
缺點:複雜的業務需要複雜的SQL, 複雜分析,SQL嵌套較多。機器學習較難實現。
Hive和SparkSQL的對比
Hive是將sql轉化成MapReduce進行計算
SparkSQL是將sql轉化成rdd集進行計算
SparkSQL中的兩個抽象
什麼RDD??
彈性分佈式數據集。
DataFrame
什麼是DataFrame??
DataFrame是以RDD爲基礎的帶有Schema元信息的分佈式數據集。
(DataFrame=Schema+RDD*n)
什麼是DataSaet??
含有類型信息的DataFrame就是DataSet
(DataSaet=DataFrame+類型= Schema+RDD*n+類型)
DataSet包含了DataFrame的功能
Spark初體驗
SparkSQL驅動爲SparkSession
SparkSession可以執行SparkSQL也可以執行HiveSQL
在llinux中進行操作
開啓hadoop和spark
進入 spark-shell窗口
//讀取數據
val lineRDD= sc.textFile("hdfs://node01:8020/tt.txt").map(_.split(" "))
//實例樣例類(類似於表的結構)
case class Person(id:Int, name:String, age:Int)
//遍歷數據,將數據填充到樣例類中
val personRDD = lineRDD.map(x => Person(x(0).toInt, x(1), x(2).toInt))
//將RDD轉換成DataFrame
val personDF = personRDD.toDF
//查看數據
personDF.show
//輸出表結構
personDF.printSchema
//將DataFrame註冊爲張表
personDF.createOrReplaceTempView("t_person")
//通過SQL語句進行查詢
spark.sql("select id,name from t_person where id > 3").show
|
使用SparkSession對象(spark)直接讀取數據,讀取文本文件是沒有元數據信息,讀取json文件有元數據信息。
創建DataSet
1.通過spark.createDataset創建Dataset
val fileRdd = sc.textFile("hdfs://node01:8020/person.txt") //RDD[String]
val ds1 = spark.createDataset(fileRdd) //DataSet[String]
ds1.show
|
2.通RDD.toDS方法生成DataSet
case class Person(name:String, age:Int)
val data = List(Person("zhangsan",20),Person("lisi",30)) //List[Person]
val dataRDD = sc.makeRDD(data)
val ds2 = dataRDD.toDS //Dataset[Person]
ds2.showS
|
3.通過DataFrame.as[泛型]轉化生成DataSet
case class Person(name:String, age:Long)
val jsonDF= spark.read.json("file:///export/servers/spark/examples/src/main/resources/people.json")
val jsonDS = jsonDF.as[Person] //DataSet[Person]
jsonDS.show
|
SparkSQL查詢數據的形態
- 類似方法調用,領域特定語言(DSL)。
準備數據
val lineRDD= sc.textFile("hdfs://node01:8020/tt.txt").map(_.split(" "))
case class Person(id:Int, name:String, age:Int)
val personRDD = lineRDD.map(x => Person(x(0).toInt, x(1), x(2).toInt))
val personDF = personRDD.toDF
personDF.show
查詢
personDF.select("id","name","age").show
personDF.select($"id",$"name",$"age"+1).show
personDF.select($"id",$"name",$"age"+1).filter($"age">25).show
- SQL語句
註冊成一張表
personDF.createOrReplaceTempView("t_person")
查詢
spark.sql("select * from t_person").show
spark.sql("select * from personDFT ").show
spark.sql("select * from personDFT where age >25").show
總結:
1.DataFrame和DataSet都可以通過RDD來進行創建
2.也可以通過讀取普通文本創建--注意:直接讀取沒有完整的約束,需要通過RDD+Schema
3.通過josn/parquet會有完整的約束
4.不管是DataFrame還是DataSet都可以註冊成表,之後就可以使用SQL進行查詢了! 也可以使用DSL!
通過IDEA編寫SparkSQL代碼
第1種:指定列名添加Schema
第2種:通過StructType指定Schema
第3種:編寫樣例類,利用反射機制推斷Schema
第一種:指定列名添加Schema
object demo01 {
def main(args: Array[String]): Unit = {
read
}
def read={
//1.創建SparkSession對象
val spark = SparkSession.builder().master("local[*]").appName("SparkSql").getOrCreate()
//2.通過SparkSession對象獲得sparkContext讀取數據
val sc = spark.sparkContext
//3.讀取數據轉化成RDD 轉化成DF/DS
var rowRDD= sc.textFile("D:\\dev\\dashuju\\spark\\第二天\\test.txt").map(x=>{var line =x.split(" ");
//返回類型爲元組
(line(0),line(1),line(2).toInt,line(3),line(4),line(5).toInt)
})
//隱式轉換(導入隱式類) 爲對象添加原本不具有的方法
import spark.implicits._
//通過sql查詢數據
rowRDD.toDF()
val personDF = rowRDD.toDF("class","name","age","sex","subject","score")
//查看前10條 默認爲20條數據
personDF.show(10)
//查看錶結構
personDF.printSchema()
//關閉sc
sc.stop()
//關閉sc
spark.stop()
}
}
|
第二種:通過StructType指定Schema
def main(args: Array[String]): Unit = {
read
}
def read = {
//1.創建SparkSession對象
val spark = SparkSession.builder().master("local[*]").appName("SparkSql").getOrCreate()
//2.通過SparkSession對象獲得sparkContext讀取數據
val sc = spark.sparkContext
//3.讀取數據轉化成RDD 轉化成DF/DS
var rowRDD = sc.textFile("D:\\dev\\dashuju\\spark\\第二天\\test.txt").map(x => {
var line = x.split(" ");
//利用 StructType指定Schema 的時候要將類型轉化成Row
Row(line(0), line(1), line(2).toInt, line(3), line(4), line(5).toInt)
})
//隱式轉換 爲對象添加原本不具有的方法
import spark.implicits._
//通過StructType指定Schema
val schema: StructType = StructType(Seq(
StructField("class", StringType, true), //允許爲空
StructField("name", StringType, true),
StructField("age", IntegerType, true),
StructField("sex", StringType, true), //允許爲空
StructField("subject", StringType, true),
StructField("score", IntegerType, true))
)
//將數據rdd和通過StructType指定Schema 放入
val dataFrame = spark.createDataFrame(rowRDD, schema)
dataFrame.show()
dataFrame.printSchema()
//關閉sc
sc.stop()
//關閉sc
spark.stop()
}
|
第三種:編寫樣例類,利用反射機制推斷Schema
//首先實例一個樣例類
case class Student(classs:String,name:String,age:Int,sex:String,subject:String,score:Int)
def main(args: Array[String]): Unit = {
read
}
def read={
//1.創建SparkSession對象
val spark = SparkSession.builder().master("local[*]").appName("SparkSql").getOrCreate()
//2.通過SparkSession對象獲得sparkContext讀取數據
val sc = spark.sparkContext
//3.讀取數據轉化成RDD 轉化成DF/DS
var rowRDD= sc.textFile("D:\\dev\\dashuju\\spark\\第二天\\test.txt").map(x=>{var line =x.split(" ");
//返回的是一個樣例類對象
Student(line(0),line(1),line(2).toInt,line(3),line(4),line(5).toInt)
})
//隱式轉換 爲對象添加原本不具有的方法
import spark.implicits._
//通過sql查詢數據
rowRDD.toDF()
val personDF = rowRDD.toDF()
//查看前10條
personDF.show(10)
//查看錶結構
personDF.printSchema()
//關閉sc
sc.stop()
//關閉sc
spark.stop()
}
|
SparkSQL查詢的方式
1、SQL查詢的方式
//0.註冊表
personDF.createOrReplaceTempView("t_person")
//1.查詢所有數據
spark.sql("select * from t_person").show()
2、DSL查詢的方式
//1.查詢所有數據
personDF.select("name","age")
//2.查詢age+1
personDF.select($"name",$"age" + 1)
RDD DF DS(比DF多了類型) 三者之間的轉化
//1.RDD-->DF val personDF: DataFrame = personRDD.toDF
//2.DF-->RDD val rdd: RDD[Row] = personDF.rdd //3.RDD-->DS val DS: Dataset[Person] = personRDD.toDS()
//4.DS-->RDD val rdd2: RDD[Person] = DS.rdd //5.DF-->DS(DS比DF多了類型) val DS2: Dataset[Person] = personDF.as[Person]
//6.DS-->DF val DF: DataFrame = DS2.toDF()
.toDF
.rdd
.toDS
.as[Person]
|
Spark SQL完成WordCount
SQL風格
package cn.itcast.sql
import org.apache.spark.SparkContext import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}
object WordCount {
def main(args: Array[String]): Unit = {
//1.創建SparkSession val spark: SparkSession = SparkSession.builder().master("local[*]").appName("SparkSQL").getOrCreate()
val sc: SparkContext = spark.sparkContext
sc.setLogLevel("WARN")
//2.讀取文件的兩種方式 val fileDF: DataFrame = spark.read.text("D:\\data\\words.txt")
val fileDS: Dataset[String] = spark.read.textFile("D:\\data\\words.txt")
//3.對每一行按照空格進行切分 import spark.implicits._
val wordDS: Dataset[String] = fileDS.flatMap(_.split(" "))//注意:正確,因爲DS有泛型,知道_是String //4.對上面的數據進行WordCount wordDS.createOrReplaceTempView("t_word")
val sql =
""" |select value ,count(value) as count |from t_word |group by value |order by count desc """.stripMargin
spark.sql(sql).show()
sc.stop() spark.stop() } }
|
DSL風格
package cn.itcast.sql
import org.apache.spark.SparkContext import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}
object WordCount2 {
def main(args: Array[String]): Unit = {
//1.創建SparkSession val spark: SparkSession = SparkSession.builder().master("local[*]").appName("SparkSQL").getOrCreate()
val sc: SparkContext = spark.sparkContext
sc.setLogLevel("WARN")
//2.讀取文件的兩種方式 val fileDF: DataFrame = spark.read.text("D:\\data\\words.txt")
val fileDS: Dataset[String] = spark.read.textFile("D:\\data\\words.txt")
//3.對每一行按照空格進行切分 import spark.implicits._
val wordDS: Dataset[String] = fileDS.flatMap(_.split(" "))//注意:正確,因爲DS有泛型,知道_是String //4.對上面的數據進行WordCount wordDS.groupBy("value").count().orderBy($"count".desc).show()
sc.stop() spark.stop() } }
|
Spark SQL多數據源交互
Spark SQL可以與多種數據源交互,如普通文本、json、parquet、csv、MySQL等
1.寫入mysql數據庫中不同數據源
2.從mysql數據庫中讀取不同數據源
def main(args: Array[String]): Unit = {
write
read
}
def write = {
//1.創建SparkSession對象
val spark = SparkSession.builder().master("local[*]").appName("paydata").getOrCreate()
//2.轉化成sparkContext 按照題目要求來存入數據庫
var sc = spark.sparkContext.textFile("D:\\dev\\dashuju\\spark\\第四天\\4.8號練習題32道\\paydata.txt")
.map(x => {
var line = x.split("\t");
(line(0), line(1), line(2), line(3), line(4), line(5), line(6).toInt, line(7), line(8), line(9))
})
//3.將RDD轉化成DF
import spark.implicits._
val dataFrame = sc.toDF("oid", "posname", "ordernum", "paychannel", "paymethod", "posid", "money", "paytime", "ordstatus", " recstate")
//4.配置文件Properties配置文件
val prop = new Properties()
prop.put("user", "root")
prop.put("password", "root")
var url = "jdbc:mysql://localhost:3306/sparkmysql?useUnicode=true&characterEncoding=UTF-8"
//5.將數據寫入
//mode參數 Overwrite覆蓋 paydata表名 url路徑 prop:Properties配置文件
//personDF.write.text("D:\\data\\output\\text") 寫入text的格式到text文件
dataFrame.write.mode(SaveMode.Overwrite).jdbc(url, "paydata", prop)
spark.close()
}
def read = {
//1.創建SparkSession對象
val spark = SparkSession.builder().appName("paydata").master("local[*]").getOrCreate()
//2.實例sparkContext
val sc = spark.sparkContext
//3.設置Properties配置文件
val prop = new Properties()
prop.put("user", "root")
prop.put("password", "root")
var url = "jdbc:mysql://localhost:3306/sparkmysql?useUnicode=true&characterEncoding=UTF-8"
//4.實例個dataFrame
val dataFrame = spark.read.jdbc(url, "paydata", prop)
//幾種讀取的方法
//spark.read.json("D:\\data\\output\\json").show()
//spark.read.csv("D:\\data\\output\\csv").toDF("id","name","age").show()
// spark.read.parquet("D:\\data\\output\\parquet").show()
//5.創建一個表 根據業務需求寫數據
dataFrame.createOrReplaceTempView("paydata")
//SQL查詢,還可以使用DSL查詢 //DLS查詢age+1 personDF.select($"name",$"age" + 1)
// //18、計算出商戶名稱爲33333328的所有金額爲多少?
// spark.sql("SELECT SUM(money) FROM paydata WHERE posname ='33333328' ").rdd.foreach(println(_))
// //19、計算出支付通道爲alipay的所有金額?
// spark.sql("SELECT SUM(money) FROM paydata WHERE paychannel='alipay'").rdd.foreach(println(_))
// //20、計算出已支付的所有金額爲多少?
// spark.sql("SELECT SUM(money) FROM paydata WHERE ordstatus ='已支付' ").rdd.foreach(println(_))
// //21、計算出支付通道爲alipay和wxpay這兩種方式各自的所有金,比較那個方式的金額更多?
// var snm01= spark.sql("SELECT SUM(money) FROM paydata WHERE paychannel='alipay'").rdd.first().get(0).toString.toInt
// var snm02= spark.sql("SELECT SUM(money) FROM paydata WHERE paychannel='wxpay'").rdd.first().get(0).toString.toInt
// if (snm01>snm02){
// println("支付通道爲alipay的所有金更多")
// }else{
// println("支付通道爲wxpay的所有金更多")
// }
//6.關閉
spark.close()
println(sc)
}
|
總結
1.SparkSQL寫數據:
DataFrame/DataSet.write.json/csv/jdbc
2.SparkSQL讀數據:
SparkSession.read.json/csv/text/jdbc/format
自定義函數分類
類似於hive當中的自定義函數, spark同樣可以使用自定義函數來實現新的功能。
spark中的自定義函數有如下3類
1.UDF(User-Defined-Function)
輸入一行,輸出一行
2.UDAF(User-Defined Aggregation Funcation)
輸入多行,輸出一行
3.UDTF(User-Defined Table-Generating Functions)
輸入一行,輸出多行
自定義UDF
有udf.txt數據格式如下:
Hello
abc
study
small
通過自定義UDF函數將每一行數據轉換成大寫
select value,smallToBig(value) from t_word
代碼演示
package cn.itcast.sql
import org.apache.spark.SparkContext import org.apache.spark.sql.{Dataset, SparkSession}
object UDFDemo {
def main(args: Array[String]): Unit = {
//1.創建SparkSession val spark: SparkSession = SparkSession.builder().master("local[*]").appName("SparkSQL").getOrCreate()
val sc: SparkContext = spark.sparkContext
sc.setLogLevel("WARN")
//2.讀取文件 val fileDS: Dataset[String] = spark.read.textFile("D:\\data\\udf.txt") fileDS.show()
/* +----------+ | value| +----------+ |helloworld| | abc| | study| | smallWORD| +----------+ */ /* 將每一行數據轉換成大寫 select value,smallToBig(value) from t_word */ //註冊一個函數名稱爲smallToBig,功能是傳入一個String,返回一個大寫的String
//register爲參數 smallToBig爲使用時的名字自定義 類型爲str:String 將沒行單詞轉化爲大寫str.toUpperCase() 如果業務需求過多可以=》後面加{} spark.udf.register("smallToBig",(str:String) => str.toUpperCase()) fileDS.createOrReplaceTempView("t_word")
//使用我們自己定義的函數 spark.sql("select value,smallToBig(value) from t_word").show()
/* +----------+---------------------+ | value|UDF:smallToBig(value)| +----------+---------------------+ |helloworld| HELLOWORLD| | abc| ABC| | study| STUDY| | smallWORD| SMALLWORD| +----------+---------------------+ */ sc.stop() spark.stop() } }
|
|
自定義UDAF[瞭解]
需求
有udaf.json數據內容如下
{"name":"Michael","salary":3000}
{"name":"Andy","salary":4500}
{"name":"Justin","salary":3500}
{"name":"Berta","salary":4000}
求取平均工資
繼承UserDefinedAggregateFunction方法重寫說明
inputSchema:輸入數據的類型
bufferSchema:產生中間結果的數據類型
dataType:最終返回的結果類型
deterministic:確保一致性,一般用true
initialize:指定初始值
update:每有一條數據參與運算就更新一下中間結果(update相當於在每一個分區中的運算)
merge:全局聚合(將每個分區的結果進行聚合)
evaluate:計算最終的結果
代碼演示
package cn.itcast.sql
import org.apache.spark.SparkContext import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction} import org.apache.spark.sql.types._ import org.apache.spark.sql.{DataFrame, Row, SparkSession}
object UDAFDemo {
def main(args: Array[String]): Unit = {
//1.獲取sparkSession val spark: SparkSession = SparkSession.builder().appName("SparkSQL").master("local[*]").getOrCreate()
val sc: SparkContext = spark.sparkContext
sc.setLogLevel("WARN")
//2.讀取文件 val employeeDF: DataFrame = spark.read.json("D:\\data\\udaf.json")
//3.創建臨時表 employeeDF.createOrReplaceTempView("t_employee")
//4.註冊UDAF函數 spark.udf.register("myavg",new MyUDAF)
//5.使用自定義UDAF函數 spark.sql("select myavg(salary) from t_employee").show()
//6.使用內置的avg函數 spark.sql("select avg(salary) from t_employee").show() } } class MyUDAF extends UserDefinedAggregateFunction{
//輸入的數據類型的schema override def inputSchema: StructType = {
StructType(StructField("input",LongType)::Nil) }
//緩衝區數據類型schema,就是轉換之後的數據的schema override def bufferSchema: StructType = {
StructType(StructField("sum",LongType)::StructField("total",LongType)::Nil) }
//返回值的數據類型 override def dataType: DataType = {
DoubleType }
//確定是否相同的輸入會有相同的輸出 override def deterministic: Boolean = {
true }
//初始化內部數據結構 override def initialize(buffer: MutableAggregationBuffer): Unit = {
buffer(0) = 0L
buffer(1) = 0L
}
//更新數據內部結構,區內計算 override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
//所有的金額相加 buffer(0) = buffer.getLong(0) + input.getLong(0)
//一共有多少條數據 buffer(1) = buffer.getLong(1) + 1
}
//來自不同分區的數據進行合併,全局合併 override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
buffer1(0) =buffer1.getLong(0) + buffer2.getLong(0)
buffer1(1) = buffer1.getLong(1) + buffer2.getLong(1) }
//計算輸出數據值 override def evaluate(buffer: Row): Any = {
buffer.getLong(0).toDouble / buffer.getLong(1) } }
|
開窗函數
介紹
開窗函數的引入是爲了既顯示聚集前的數據,又顯示聚集後的數據。即在每一行的最後一列添加聚合函數的結果。
開窗用於爲行定義一個窗口(這裏的窗口是指運算將要操作的行的集合),它對一組值進行操作,不需要使用 GROUP BY 子句對數據進行分組,能夠在同一行中同時返回基礎行的列和聚合列。
聚合函數和開窗函數
聚合函數是將多行變成一行,count,avg....
開窗函數是將一行變成多行;
聚合函數如果要顯示其他的列必須將列加入到group by中
開窗函數可以不使用group by,直接將所有信息顯示出來
開窗函數分類
1.聚合開窗函數
聚合函數(列) OVER(選項),這裏的選項可以是PARTITION BY 子句,但不可以是 ORDER BY 子句。
2.排序開窗函數
排序函數(列) OVER(選項),這裏的選項可以是ORDER BY 子句,也可以是 OVER(PARTITION BY 子句 ORDER BY 子句),但不可以是 PARTITION BY 子句。
準備工作
/export/servers/spark/bin/spark-shell
//實例一個樣例類分數
case class Score(name: String, clazz: Int, score: Int)
//存入些數據 轉化成DF
val scoreDF = spark.sparkContext.makeRDD(Array(
Score("a1", 1, 80),
Score("a2", 1, 78),
Score("a3", 1, 95),
Score("a4", 2, 74),
Score("a5", 2, 92),
Score("a6", 3, 99),
Score("a7", 3, 99),
Score("a8", 3, 45),
Score("a9", 3, 55),
Score("a10", 3, 78),
Score("a11", 3, 100))
).toDF("name", "class", "score")
scoreDF.show()
聚合開窗函數
示例1
OVER 關鍵字表示把聚合函數當成聚合開窗函數而不是聚合函數。
SQL標準允許將所有聚合函數用做聚合開窗函數。
spark.sql("select count(name) from scores").show
spark.sql("select name, class, score, count(name)over()name_count from scores").show
查詢結果如下所示:
OVER 關鍵字後的括號中還可以添加選項用以改變進行聚合運算的窗口範圍。
如果 OVER 關鍵字後的括號中的選項爲空,則開窗函數會對結果集中的所有行進行聚合運算。
開窗函數的 OVER 關鍵字後括號中的可以使用 PARTITION BY 子句來定義行的分區來供進行聚合計算。與 GROUP BY 子句不同,PARTITION BY 子句創建的分區是獨立於結果集的,創建的分區只是供進行聚合計算的,而且不同的開窗函數所創建的分區也不互相影響。
下面的 SQL 語句用於顯示按照班級分組後每組的人數:
OVER(PARTITION BY class)表示對結果集按照 class 進行分區,並且計算當前行所屬的組的聚合計算結果。
//按照class進行分區,分區返回class分區後的總數,並按照開窗函數顯示出來
spark.sql("select name, class, score, count(name) over(partition by class) name_count from scores").show
查詢結果如下所示:
ROW_NUMBER順序排序
row_number() over(order by score) as rownum 表示按score 升序的方式來排序,並得出排序結果的序號
注意:
在排序開窗函數中使用 PARTITION BY 子句需要放置在ORDER BY 子句之前。
●示例1
//按照分數進行排序,並且按開窗函數顯示出來
spark.sql("select name, class, score, row_number() over(order by score) rank from scores").show()
//按照相同class進行分區,然後按照分區後的分數排序
spark.sql("select name, class, score, row_number() over(partition by class order by score) rank from scores").show()
RANK跳躍排序
rank() over(order by score) as rank表示按 score升序的方式來排序,並得出排序結果的排名號。
這個函數求出來的排名結果可以並列(並列第一/並列第二),並列排名之後的排名將是並列的排名加上並列數
簡單說每個人只有一種排名,然後出現兩個並列第一名的情況,這時候排在兩個第一名後面的人將是第三名,也就是沒有了第二名,但是有兩個第一名
//按照分數進行排序
spark.sql("select name, class, score, rank() over(order by score) rank from scores").show()
//按照相同的class進行分區,然後按照分數進行排序 如果相同排序的序號相同,下一個序號就加上相同的個數
spark.sql("select name, class, score, rank() over(partition by class order by score) rank from scores").show()
DENSE_RANK連續排序
dense_rank() over(order by score) as dense_rank 表示按score 升序的方式來排序,並得出排序結果的排名號。
這個函數並列排名之後的排名是並列排名加1
簡單說每個人只有一種排名,然後出現兩個並列第一名的情況,這時候排在兩個第一名後面的人將是第二名,也就是兩個第一名,一個第二名
//然後按照分數進行排序 如果相同排序的序號相同,下一個數就按照上一個數的序號加一
spark.sql("select name, class, score, dense_rank() over(order by score) rank from scores").show()
spark.sql("select name, class, score, dense_rank() over(partition by class order by score) rank from scores").show()
NTILE分組排名[瞭解]
ntile(6) over(order by score)as ntile表示按 score 升序的方式來排序,然後 6 等分成 6 個組,並顯示所在組的序號。
spark.sql("select name, class, score, ntile(6) over(partition by class order by score) rank from scores").show()
Hive查詢流程及原理
執行HQL時,先到MySQL元數據庫中查找描述信息,然後解析HQL並根據描述信息生成MR任務
Hive將SQL轉成MapReduce執行速度慢
使用SparkSQL整合Hive其實就是讓SparkSQL去加載Hive 的元數據庫,然後通過SparkSQL執行引擎去操作Hive表內的數據
所以首先需要開啓Hive的元數據庫服務,讓SparkSQL能夠加載元數據
|
Hive開啓MetaStore服務
1: 修改 hive/conf/hive-site.xml 新增如下配置
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>hive.metastore.warehouse.dir</name>
<value>/user/hive/warehouse</value>
</property>
<property>
<name>hive.metastore.local</name>
<value>false</value>
</property>
<property>
<name>hive.metastore.uris</name>
<value>thrift://node01:9083</value>
</property>
</configuration>
|
2: 後臺啓動 Hive MetaStore服務
nohup /export/servers/hive/bin/hive --service metastore &
SparkSQL整合Hive MetaStore
Spark 有一個內置的 MateStore,使用 Derby 嵌入式數據庫保存數據,但是這種方式不適合生產環境,因爲這種模式同一時間只能有一個 SparkSession 使用,所以生產環境更推薦使用 Hive 的 MetaStore
SparkSQL 整合 Hive 的 MetaStore 主要思路就是要通過配置能夠訪問它, 並且能夠使用 HDFS 保存 WareHouse,所以可以直接拷貝 Hadoop 和 Hive 的配置文件到 Spark 的配置目錄
hive-site.xml 元數據倉庫的位置等信息
core-site.xml 安全相關的配置
hdfs-site.xml HDFS 相關的配置
將上面三個文件拷貝到/export/servers/spark/conf目錄下
使用IDEA本地測試直接把以上配置文件放在resources目錄即可
hive-site.xml core-site.xml hdfs-site.xml拷貝到項目得resources中,清空target.
如果沒有msql驅動包記得下載 下載/查找mysql連接驅動包添加到spark得jars文件夾中
如下鏈接講解無msql驅動包
https://blog.csdn.net/weixin_44519124/article/details/105515411
package cn.itcast.sql
import org.apache.spark.sql.SparkSession
object HiveSupport {
def main(args: Array[String]): Unit = {
//創建sparkSession val spark = SparkSession .builder() .appName("HiveSupport") .master("local[*]")
//.config("spark.sql.warehouse.dir", "hdfs://node01:8020/user/hive/warehouse") //.config("hive.metastore.uris", "thrift://node01:9083") .enableHiveSupport()//開啓hive語法的支持 .getOrCreate() spark.sparkContext.setLogLevel("WARN")
//查看有哪些表 spark.sql("show tables").show()
//創建表 spark.sql("CREATE TABLE person (id int, name string, age int) row format delimited fields terminated by ' '")
//加載數據,數據爲當前SparkDemo項目目錄下的person.txt(和src平級) spark.sql("LOAD DATA LOCAL INPATH 'SparkDemo/person.txt' INTO TABLE person")
//查詢數據 spark.sql("select * from person ").show()
spark.stop() } }
|