Spark學習筆記
Data
Source->Kafka->Spark Streaming->Parquet->Spark SQL(SparkSQL可以結合ML、GraphX等)->Parquet->其它各種Data Mining等
1.1 Spark集群的安裝
Spark的運行是構建在hadoop集群之上(默認hadoop集群已經安裝好了),在spark集群集群上必須要安裝對應版本的scala
1.1.1 scala安裝
?下載scala版本,解壓scala
?配置環境變量/etc/profile,添加SCALA_HOME、修改PATH,添加上scala的path路徑
?進入$SCALA_HOME/bin目錄,執行./scala驗證scala是否安裝成功
?集群機器都需要安裝scala
1.1.2 spark安裝
在集群的所有機器上都必須要安裝spark,首先安裝master的spark程序
?先解壓spark程序
?修改環境變量/etc/profile添加SPARK_HOME和修改spark PATH路徑
?配置spark,進入conf目錄下
nmv spark-env.sh.template spark-env.sh
其中:spark_master_ip:用于指定master
nvi slaves修改文件,把work節點都添加進去;
?至此,spark集群安裝完畢
1.1.3啟動集群校驗
?先啟動hadoop集群,jps查看進程
?再啟動spark集群,在sbin目錄下執行./start-all.shjps查看進程
?Ui訪問,檢查集群情況http://master:8080
?進入spark/bin目錄下,啟動spark-shell腳本
1.2 spark-shell的使用
在master機器上的$SPARK_HOME/bin目錄下,運行./spark-shell程序啟動shark-shell腳本;通過http://master:4040查看spark-shell運行情況
1.2.1 spark-shell操作hdfs文件實戰
?將spark目錄下的README.md文件上傳到hdfs上的/test目錄下,通過hdfs ui來進行查看slave:50070/explorer.html#/查看文件是否上傳成功
?在spark-shell腳本程序下,執行sc(SparkContext實例),啟動spark-sehll時,系統自動生成
scala> sc
res0: org.apache.spark.SparkContext = org.apache.spark.SparkContext@65859b44
# SparkContext是把代碼提交到集群或者本地的通道,編寫Spark代碼,無論是要運行本地還是集群都必須要有SparkContext實例
?Spark-shell讀取hdfs文件的README.md文件
val file = sc.textFile(“hdfs://mapeng:8020/test/README.md”)
#這里把讀取到的文本內容賦值給了變量file,(就是一個MappedRDD,在spark的代碼中,一切都是基于RDD進行操作的)
?讀取文本中包含有“spark”的行
scala> val sparks = file.filter(line
=> line.contains("spark"))
sparks: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[3] at
filter at :26
#此時生成了一個FilterRDD
?統計spark一共出現多少次
Sparks.count
1.2.2 spark-shell操作及詳細說明
1.2.2.1并行化集合(parallelize)
?加載集合數據
val data = sc.parallelize(1 to 10)#加載集合數據
或者; val data = sc.parallelize(List(1,2,3,4…))
?對集合數據進行*2操作
val data1 = data.map(_*2)
?對數據進行過濾:過濾出是2的倍數的集合
val data2 = data.filter(_%2==0)
?內存緩存數據
data.cache
?觸發action,以數據的形式返回結果集
data.collect
?返回結果集的第一個元素
data.first
?返回結果集的前3個元素
data.take(3)
?統計元素的個數
data.count
?查看RDD的轉換過程
data.toDebugString
1.2.2.2 map數據集合
?加載List(Map)數據
val
data=sc.parallelize(List(("A",1),("B",2),("C",3),("A",4),("B",5)))
?排序sortByKey()
scala> data.sortByKey().collect
res55: Array[(String, Int)] = Array((A,1), (A,4), (B,2), (B,5),
(C,3))
?分組groupByKey()
scala>
data.groupByKey().collect
res57: Array[(String, Iterable[Int])] = Array((B,CompactBuffer(2,? 5)), (A,CompactBuffer(1, 4)), (C,CompactBuffer(3)))
?求和reduceByKey(_+_)
scala>
data.reduceByKey(_+_).collect
res59: Array[(String, Int)] = Array((B,7), (A,5), (C,3))
?去重distinct
scala> data.distinct.collect
res60: Array[(String, Int)] = Array((A,1), (A,4), (B,5), (C,3),
(B,2))
?聯合union
scala> val
data1=sc.parallelize(List(("A",1),("B",2),("C",3),("A",4),("B",5)))
data1:
org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[0] at
parallelize at :24
scala> val data2=sc.parallelize(List(("A",4),("A",4),("C",3),("A",4),("B",5)))
data2:
org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[1] at
parallelize at :24
scala>
data1.union(data2).collect
res1: Array[(String, Int)] = Array((A,1), (B,2), (C,3), (A,4),
(B,5), (A,4), (A,4), (C,3), (A,4), (B,5))
?關聯join
相當于笛卡爾積
1.2.2.3保存轉換結果saveAsTextFile
data.saveAsTextFile(“path”);//將轉換結果存儲在hdfs指定的路徑
1.2.3 spark cache緩存
對于spark程序,第二次執行要比前面的執行的效率要高
1.3 RDD(彈性分布式數據集)
1.3.1 RDD介紹
?RDD是一個容錯的、并行的數據結構,可以讓用戶顯示的將數據存儲在磁盤和內存中,并能控制數據的分區。
?RDD提供了一套豐富的函數來操作數據
?RDD作為數據機構,本質上是一個只讀的分區記錄集合;一個RDD可以包含多個分區,每個分區就是一個dataSet片段;RDD可以相互依賴
n窄依賴:RDD的每個分區最多只能被一個child
RDD的分區使用(例如:map操作)
n寬依賴:RDD的分區可以被多個child
RDD的分區使用(例如:join操作)
區別:
(1)窄依賴可以在集群中的一個節點上如流水般的執行,相反,寬依賴需要所有的父分區的數據都可用
(2)從出現失敗恢復的角度來考慮:窄依賴只需要重新計算失敗的父RDD的分區,而寬依賴失敗會導致其父RDD的多個分區重新計算
1.3.2 RDD分區
1.3.3創建操作
1.3.3.1集合創建操作
Spark提供了兩類函數實現從集合生成RDD;
?parallelize
val rdd = sc.parallelize(1 to 100)
?makeRDD:還提供了指定分區參數
val rdd = sc.makeRDD(1 to 100,3)#指定了分區數為3
1.3.3.2存儲創建操作
操作hdfs
val rdd = sc.textFile(“hdfs://master:9000/test/xxx.txt”)
1.3.4 RDD的基本轉換操作
1.3.4.1 RDD的重新分區
repartition和coalesce是對RDD的分區進行重新劃分
?repartition(numPartitions:Int):RDD[T]
?coalesce(numPartitions:Int,shuffle:Boolean=false):RDD[T]
repartition只是coalesce接口中shuffle為true的簡易實現。
重新劃分分區主要有三種情況:(原RDD有N個分區,需要重新劃分為M個分區)
?N
?N>M(相差不大):面臨著要把原分區進行合并的操作,最終合成M個分區,這時將shuffle設置為false
注:在shuffle為false時,設置M>N,coalesce是不起作用的
?N>>M(差距懸殊):如果將shuffle設置為false,由于父子RDD是窄依賴,會使得它們同處于一個stage中,可能會造成spark程序運行的并行度不夠,從而影響效率。
因而,最好設置為true,使得coalesce之前的操作有更好的并行度
1.3.4.2 RDD轉換為數組(randomSplit、glom)
?randomSplit(weight:Array[Double],seed:Long=System.nanoTime):Array[RDD[T]]
randomSplit函數是將一個RDD切分為多個RDD,返回結果是一個RDD數組;函數的第一個方法傳入的參數權重是一個Double類型的數組;權重大的,分到的數據的概率大
val rdd = sc.makeRDD(1 to 10)
val splitRDD = rdd.randomSplit(Array(1.0,3.0,6.0))
#返回的一個RDD數組,查看數組元素
splitRDD(0).collect
splitRDD(1).collect
splitRDD(2).collect
?glom():RDD[Array[T]]
glom函數是將RDD中每一個分區中類型為T的元素轉換為Array[T]
val rdd = sc.makeRDD(1 to 10,3)
val glomRDD= rdd.glom
#返回的結果是一個數組,
glomRDD.collect
scala> glomRDD.collect
res44: Array[Array[Int]] = Array(Array(1, 2, 3), Array(4, 5, 6), Array(7,
8, 9, 10))
1.3.4.3 RDD的集合操作
?union(other:RDD[T]) :RDD[T]
將兩個RDD的數據進行合并,返回兩個RDD的并集,不去重
?intersection(other:RDD[T]) :RDD[T]
返回兩個RDD的交集(會去重)
?subtract(other:RDD[T]) :RDD[T]
取差集
?zip(other:RDD[T]):RDD[T]
zip函數用于將兩個RDD組合成key/value形式的RDD,兩個RDD的partition個數以及元素的數量都必須要相同,不然會拋出異常
val rdd1 = sc.makeRDD(List(1,2,3,3))
val rdd2 = sc.makeRDD(List(2,3,4))
union操作
rdd1.union(rdd2).collect
#結果
Array[Int] = Array(1, 2, 3, 3, 2, 3, 4)
intersection操作
rdd1.intersection(rdd2).collect
#結果(去重)
Array[Int] = Array(3, 2)
subtract操作
rdd1.subtract(rdd2).collect
#結果(不去重)
Array[Int] = Array(1, 1)
zip操作
scala> val rdd1 = sc.makeRDD(1
to 3)
scala> val rdd2 =
sc.makeRDD(List(1.0,2.0,3.0))
scala> rdd1.zip(rdd2).collect
res8: Array[(Int, Double)] = Array((1,1.0), (2,2.0), (3,3.0))
1.3.4.4鍵值RDD轉換操作
map和flatmap的區別:
(1)map是對每個元素都進行指定的操作,返回每個元素處理后的對象
(2)flatmap對所有的元素都做指定的操作,將所有的對象合并為一個對象返回
val rdd = sc.makeRDD(1 to 3)
rdd.map(x=>Seq(x,x)).collect
#結果
Array[Seq[Int]] = Array(List(1, 1),
List(2, 2), List(3, 3), List(4, 4))
rdd.flatMap(x=>Seq(x,x)).collect
#結果,合并為一個對象返回
Array[Int] = Array(1, 1, 2, 2, 3, 3, 4,? 4)
未完待續.....
1.3.5 RDD的行動操作
每調用一次行動操作,都會觸發一次spark的調度并返回響應的結果
1.3.5.1集合標量行動操作
?count
返回RDD中的元素的個數
?first
返回RDD中的第一個元素
?reduce(f:(T,T) = >T)
對RDD中的元素進行二元計算,返回計算結果
val rdd = sc.makeRDD(1 to 4)
rdd.reduce(_+_)#10
rdd.reduce(_-_)#-8
?collect()
以集合的形式返回RDD的元素
?take(number:Int)
返回集合中[0,num-1]下標的元素
?top(num:Int)
先降序排序,返回前num個元素
?takeOrdered(num:Int)
以與top相反的排序規則(升序),返回前num個元素
?lookup(key:k):Seq[v]
lookup是針對(k,v)類型RDD的行動操作,針對給定的鍵值,返回與此鍵值相對應的所有值
1.3.5.2存儲行動操作
RDD不僅可以存儲在hdfs中還能存儲到Hbase、MangoDB等數據庫中
1.4 Spark SQL
1.4.1 spark sql與shark區別
sparksql是一個支持結構化數據處理的spark模塊,提供DaraFrame作為可編程的數據抽象,可以對DataFrame執行sql的操作。
spark sql的誕生就是為了解決spark平臺上的交互式查詢問題,并且提供sql接口兼容原有數據庫用戶的使用情況
?shark簡單的說,就是spark上的hive,其底層依賴Hive引擎的,但在spark平臺上,解析速度是hive的好多倍;就是一個升級版的大數據倉庫
?在spark1.0版本開始,shark被官方拋棄使用
?spark sql的優勢:
nspark sql完全脫離了hive的限制
nspark sql支持查詢原生的RDD,能夠高效的處理大數據的各種場景的基礎
n能夠在scala中寫sql語句,支持簡單的sql語法檢查,將結果取回作為RDD使用
nCatalyst能夠幫助用戶優化查詢,catalyst能夠進行一定程度的性能提升
# catalyst是spark sql的調度核心,解析sql形成其對應的執行計劃(遵循DAG圖)
1.4.2 DataFrame和DataSet
1.4.2.1 RDD與DataFrame的區別
如上圖:
?左側的RDD[Person]雖然以Person為類型參數,但是spark框架本省不了解Person類的內部結構;而右側的DataFrame卻提供了詳細的結構信息(schema),使得spark sql可以清楚的知道該數據集中包含哪些列,每列的名稱和類型
?RDD是分布式的java對象的集合;而DataFrame是分布式的row對象的集合
?DataFrame除了提供比RDD更豐富的算子外,更重要的特點是提升執行效率、減少數據讀取以及執行計劃的優化
1.4.2.2 DataFrame與DataSet的區別
DataSet可以認為是DataFrame的一個特例,主要區別是DataSet的每一個record存儲的是一個強類型值而不是一個Row。具有三個特點:
?DataSet在編譯時檢查類型
?面向對象的編程接口
?后面的版本DataFrame是繼承DataSet的,DataFrame是面向Spark sql的接口
相互轉換:
DataFrame和DataSet可以相互轉化,df.as[ElementType]這樣可以把DataFrame轉化為DataSet,ds.toDF()這樣可以把DataSet轉化為DataFrame
1.4.2.3 DataFrame
(1)DataFrame是一個分布式的數據集,類似于關系數據庫的一個表。
?DataFrame以列的形式存儲,但是不知道列的類型,因此,在編譯時不進行校驗,只有在運行時才會處理;DataSet不僅知道字段,還知道類型,所以編譯時會進行類型校驗
?可以由結構化的數據轉換過來,也可以從hive,外部數據庫或者RDD轉換
?DataFrame在spark sql中,可以使用sql的方式進行操作,與RDD類似,也可以采用lazy的方式,只有動作發生時才會真正的計算
?DataFrame的數據源:支持JSON文件、hive表格,支持本地文件系統以及hdfs等;配合JDBC還支持外部關系型數據庫
1.4.2.4與RDD的相互操作
spark sql支持兩種不同的方式用于將存在的RDD轉換為DataSets、DataFrame
?反射推斷模式:
該模式使得代碼更加的簡練,不過在寫spark程序的時候已經知道模式信息,(比如RDD中自己定義的case class類型)
練習:從hdfs文件中讀取數據,創建一個Person的RDD
1.定義Person type
scala> case class
Person(id:Int,name:String,addr:String)
defined class Person
2.從hdfs讀取文件,封裝成DataFrame數據集
scala> val personDf = spark.sparkContext
.textFile("/test/preson.txt")
.map(_.split(","))
.map(p=>Person(p(0).toInt,p(1),p(2)))
.toDF
personDf:
org.apache.spark.sql.DataFrame = [id: int, name: string ... 1 more field]
3.將personDf注冊為一個視圖view
scala>
personDf.createOrReplaceTempView("person")
4.通過sql查詢視圖;sql支持復雜的,包括多表關聯
scala> spark.sql("select
addr,count(1) from person group by addr").show
?編程指定模式:
構造一個模式,將其應用到一個已經存在的RDD上將其轉化為DataFrame,該方法適用于運行之前不知道列以及列的類型的情況
import org.apache.spark.sql.types._
1.加載數據
val presonRDD= spark.sparkContext.textFile(“/test/person.txt”)
2.定義schema
val stringSchema = “id,name,addr”
val schema = StructType(stringSchma.split(",").map(field=>StructField(field,StringType,nullable=true)))
3.轉換rdd的記錄到rows集合
1.4.3 spark sql的操作
1.4.3.1創建SparkSession實例
SparkSeesion類時Spark SQL的所有功能的入口;spark-shell啟動時,默認生成了一個SparkSession的實例:spark
importorg.apache.spark.sql.SparkSession
val spark = SparkSession.builder
.master("local")
.config("spark.sql.warehouse.dir", "/user/hive/warehouse")
.appName("spark text")
.getOrCreate
//包含隱式轉換(比如講RDDs轉成DataFrames)API
1.4.3.2創建DataFrame
spark sql讀取hdfs中json數據
val df = spark.read.json("/test/course.json")
#顯示df的數據
?顯示df的數據
df.show
#結果:
?查詢df的結構信息
?顯示指定的字段值:使用select(col1,,col2)
?過濾,查詢長度》12的數據
?分組操作groupBy
1.4.3.3 spark sql實戰
1.4.3.3.1入口:SQLContext,HiveContext(Starting Point: SQLContext)
spark sql中所有的操作入口點都是SQLContext類或者它的子類.創建一個基本的SQLContext,只需要SparkContext即可(sc)
注:spark2.0之后,sparkSession是實現了同樣的功能,不需要顯示的創建SparkConf、sparkContext、SQLContext,因為這些對象都封裝在了SparkSession中。即是
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
valhiveContext =neworg.apache.spark.sql.hive.HiveContext(sc)
除了SparkContext外,還有HiveContext。兩者的區別:
?SQLContext只支持標準的sql語法解析器
?HiveContext現在支持sql語法解析器和HiveSql語法解析器;默認為hivesql語法解析器,用戶可以通過配置來切換sql語法解析器,來運行hivesql不支持的語法
?使用HiveContext可以使用Hive的UDF,讀寫Hive表數據等hive操作。sqlContext不可以對hive進行操作
?趨勢:SqlContext不斷豐富中,最終兩者會形成一個統一的Context
1.4.3.3.2創建DataFrame
使用SqlContext,spark程序可以通過RDD、hive表、JSON格式數據等數據源創建DataFrame
val df = sqlContext.read.json(“/test/readme.json”)
1.4.3.3.3 DataFrame操作
df.show
df.printSchema
df.
1.4.3.3.4 Parquet文件
Parquet文件是一種列式存儲格式的文件,能被很多數據處理系統支持。Spark SQL支持讀取和寫入Parquet文件,并可自動保留原始數據的格式(schema)
優勢:
可以跳過不符合條件的數據,只讀取需要的數據,降低IO數據量
壓縮編碼可以降低磁盤存儲空間。由于同一列的數據類型是一樣的,可以使用更高效的壓縮編碼進一步節省存儲空間
只讀取需要的列,支持向量運算,能夠獲取更好的掃描性能
還有,parquet數據源支持自動發現和推斷分區信息
1.4.3.3.5 DataFrame的java操作
將DataFrame的結果轉換為java的list
List listRow =result.javaRDD().collect();
for(Row row : listRow){
System.out.println(row);
}
1.4.3.4 SparkSession操作
在2.0版本之前,與spark交互之前必須創建SparkConf和SparkContext;然而到了2.0版本,不需要顯示的創建這些對象SparkConf、SparkContext和SqlContext了,這些對象都已經封裝在了SparkSession中了,即,2.0版本之后,入口就是就變成了SparkSession,在spark-shell啟動時,會實例化一個SparkSession實例spark
?SparkSession封裝的對象
?獲取conf默認配置,可以調整配置spark的運行參數
1.4.3.4.0 SparkSession的創建
sparkSession類是所有Spark SQL功能的入口,只需要調用SparkSession.builder()即可創建
importorg.apache.spark.sql.SparkSession
val spark = SparkSession.builder
.master("local")
.config("spark.sql.warehouse.dir", "/user/hive/warehouse")
.appName("spark Streaming +kafka")
.enableHiveSupport
.getOrCreate
1.4.3.4.1獲取catalog元數據
1.4.3.4.2創建Dataset和Dataframe
最簡單的辦法就是通過range方法,創建DataSet
注,range也可以有3個參數,第三個參數是間隔,默認的創建的字段:id
?top(n)操作
?對某一列進行統計操作
?通過createDataFrame創建
重新命名列名withColumnRenamed
1.4.3.4.3讀取json文件
1.4.3.4.4在SparkSession中使用Spark SQL
1.4.3.4.5數據源
spark支持多種數據源的數據,
?最簡單的加載方式是load,默認的格式為parquet文件,(可以通過spark.sql.sources.default來默認指定格式)
val df = spark.read.load(“...”)
?將DataFrame數據存儲為parquet
df.select("name","type").write.save("course.parquet")
存儲的路徑為(hdfs):path hdfs://192.168.21.144:9000/user/root/course.parquet
也可以手動指定格式,以及指定要保存的文件的格式
scala> val df =
spark.read.format("json").load("/test/course.json")
df: org.apache.spark.sql.DataFrame = [length: bigint, name: string
... 1 more field]
#指定要保存的文件的格式
scala>
df.write.format("parquet").save("course1.parquet")
1.4.3.4.6保存數據到永久表saveAsTable
DataFrame可以通過調用saveAsTable方法將數據落地到hive表中,不過對已經部署的hive不會受影響,spark會創建本地的metastore(使用derby),saveAsTable會持久化數據并指向hive metastore
saveAsTable默認會創建一個“受管理表”,意味著數據的位置都是受metastore管理的。當“受管理表”被刪除,其對應的數據也都會被刪除。
注:文件內容保存在${SPARK_HOME}/bin/spark-warehouse/tableName
scala> df.write.saveAsTable("course")
#調用,spark.sql(sql_str)比較靈活
1.4.3.4.7 spark整合hive
如果spark沒有整合hive,那么spark的元數據都是在bin目錄下,自動創建metastore_db(以derby做支撐)
整合hive后,支持spark從hive取數,永久保存數據到hive中;并支持hive的mysql作為元數據存儲數據庫
?將hive配置文件中hive-site.xml文件復制到${spark_home}/conf
?將hadoop配置文件中hdfs-site.xml和core-site.xml文件復制到${spark_home}/conf
?將hive下元數據庫mysql的驅動,復制到${spark_home}/jars下
可以將DataFrame數據永久保存到hive表中
1.5 Spark Streaming
spark2.0將流數據計算統一到了DataSet中,提出了Structured
Streaming的概念,將數據源映射為一張無限長度的表,同時將流計算的結果映射為另一張表,完全以結構化的方式去操作流數據,復用了其對象的Catalyst引擎
1.5.1 spark Streaming實戰
創建Steaming DataFrame,用來監聽host:9999獲取socket數據,并對獲取的數據進行RDD轉換操作,最后統計各個詞出現的次數
?創建socket通道
nc –lk 9999
?獲取socket通道數據(需要填寫socket的host、port)
scala>val line
=
spark.readStream.format("socket").option("host","mapeng").option("port",9999).load
?轉換操作
scala> val wordCount = line.as[String].flatMap(_.split("
")).groupBy("value").count
wordCount: org.apache.spark.sql.DataFrame
= [value: string, count: bigint]
?使用start()來啟動流式數據計算流程
scala> val query =
wordCount.writeStream.outputMode("complete").format("console").start
程序自動啟動了job計算,并在控制臺展現計算結果;(接收到socket流數據,spark自動計算,控制臺展現結果)
說明:
(1)outputMode現在有三種方式:complete ,append,update(目前只實現了前兩種)
lcomplete:每次計算完成后,都能得到全量的計算結果(每次計算都得到轉換后的最新結果集)
lappend:每次計算完成后,能拿到增量的計算結果
兩種方式的使用說明:
使用了聚合類函數才能使用complete的模式,只有簡單的使用了map,filter等轉換模式才能使用append模式,不做復雜的聚合統計運算
1.6 Spark Streaming + kafka整合
1.6.1 pom.xml文件,添加spark依賴
org.apache.hadoop
hadoop-client
2.6.0
org.apache.hadoop
hadoop-common
2.6.0
org.apache.hadoop
hadoop-hdfs
2.6.0
org.apache.spark
spark-core_2.11
2.0.0
org.apache.spark
spark-sql_2.11
2.0.0
org.apache.spark
spark-streaming_2.11
2.0.0
org.apache.spark
spark-hive_2.11
2.0.0
org.apache.spark
spark-streaming-kafka-0-10_2.11
2.0.0
org.apache.hive
hive-jdbc
1.2.1
io.netty
netty-all
4.0.29.Final
1.6.2實戰代碼
packagecom.mp.fight
importorg.apache.spark.sql.SparkSession
importorg.apache.spark.streaming.StreamingContext
importorg.apache.spark.streaming.Seconds
importorg.apache.spark.streaming._
importorg.apache.spark.streaming.kafka010.KafkaUtils
importorg.apache.spark.streaming.kafka010.LocationStrategies
importorg.apache.kafka.common.serialization.StringDeserializer
importorg.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe
importorg.apache.spark.sql.SQLContext
importorg.apache.spark.sql.SaveMode
objectTest4{
caseclassPerson(id:Int,name:String,addr:String)
defmain(args: Array[String]): Unit = {
//聲明sparkSession
valspark=SparkSession.builder
.master("local")
.appName("spark? Streaming kafkasql")
.config("spark.sql.warehouse.dir","/user/hive/warehouse")
.getOrCreate
//kafka設置
valkafkaParams=Map[String, Object](
"bootstrap.servers"->"mapeng:9092",
"key.deserializer"->classOf[StringDeserializer],
"value.deserializer"->classOf[StringDeserializer],
"group.id"->"example",
"auto.offset.reset"->"latest",
"enable.auto.commit"->(false: java.lang.Boolean)
)
//topic
valtopics=List("testmp")
//初始化StreamingContext
valssc=newStreamingContext(spark.sparkContext,Seconds(30));
//從kafka中讀取數據
valkafkaStream=KafkaUtils.createDirectStream[String,String](
ssc,
LocationStrategies.PreferConsistent,
Subscribe[String,String](topics,kafkaParams)
).map(_.value())
//kafkaStream.print
importspark.sqlContext.implicits._
//啟用sparkSql來操作DStream轉換為DataFrame
kafkaStream.foreachRDD{rdd=>{
if(rdd.isEmpty) {
println("rdd is
empty")
}else{
valperson=rdd.map(_.split(",")).map(p=>Person(p(0).toInt,p(1),p(2))).toDF
//新接收的數據,追加存儲在parquet文件中(重寫文件)
person.write.mode(SaveMode.Append).save("hdfs://mapeng:9000/test/person.parquet")
//實時統計區域人數
valdf=spark.read.load("hdfs://mapeng:9000/test/person.parquet")
df.createOrReplaceTempView("person")
valaddrCount=spark.sql("select
addr,count(1) as num from person group by addr")
//將統計結果實時回寫到parquet文件中
addrCount.write.mode(SaveMode.Overwrite).save("hdfs://mapeng:9000/test/addrCount.parquet")
//繼續做多維度統計,可以使用sparksql操作處理parquet文件
}
}
}
//啟動job
ssc.start
ssc.awaitTermination
}
}
1.6.3 DStream中foreachRDD、foreachePartition、foreach的區別
?foreachRDD:得到的是處理一個批次的數據
?foreachPartition:對一個批次的每個分區數據做處理
?foreach:每條數據處理,單個元素處理
1.6.4 spark Streaming + socket
val ssc = new StreamingContext(sparkConf,
Seconds(1))
//獲得一個DStream負責連接監聽端口:地址
val lines = ssc.socketTextStream(“192.168.21.144”, 9999)
//對每一行數據執行Split操作
val words = lines.flatMap(_.split(" "))
//統計word的數量
val pairs = words.map(word => (word, 1))
val wordCounts = pairs.reduceByKey(_ + _)
//輸出結果
wordCounts.print
ssc.start//開始
ssc.awaitTermination//計算完畢退出
1.8 parquet文件
1.8.1 parquet是面向分析型業務的列式存儲格式,有如下優勢
Parquet文件尾部存儲了文件的元數據信息和統計信息,自描述的,方便解析
1.只讀取需要的列,支持向量運算,能夠獲得更好的掃描性能
2.可以跳過不符合條件的數據,只讀取需要的數據,降低io
3.同一列的數據類型是一樣的,可以使用更高效的壓縮編碼,節約存儲磁盤
1.8.2 parquet適配多種計算框架
Parquet是語言無關的,而且不與任何一種數據處理框架綁定在一起,適配多種語言和組件,能夠與Parquet配合的組件有:
查詢引擎: Hive, Impala, Pig, Presto, Drill, Tajo, HAWQ, IBM
Big SQL
計算框架: MapReduce, Spark, Cascading, Crunch, Scalding, Kite
數據模型: Avro, Thrift, Protocol Buffers, POJOs
1.8.3 parquet數據模型
eg:
message AddressBook {
required string owner;
repeated stringownerPhoneNumbers;
repeated group contacts {
required string name;
optional string phoneNumber;
}
}
說明:
schema的格式是這樣的:
(1)根叫做message
(2)message包括有多個fields,每個fields包括有三個屬性:repetition,type,name
其中,repetition有3中類型:required(出現一次)、optional(出現0次或者1次)、repeated(出現0次或者多次)
(3)type可以是一個group或者一個簡單的類型
以上schema描述說明:
(1)每條記錄標識一個AddressBook
(2)有且只有一個owner
(3)有0個或多個ownerPhoneNumbers
(4)owner可以有0個或者多個contacts。每個contact有且只有一個name,這個contact的phoneNumber可有可無(0個或者1個)
注:parquet格式的數據類型沒有復雜的Map,List,Set等,使用group和repeated fields來表示;
null值不會被存儲
實例:
1.8.4 parquet格式文件的存儲
在parquet格式的存儲中,一個schema的樹結構有幾個葉子節點,實際存儲匯總就有幾個column,例如上圖中的schema實際存儲就4個列
1.8.5 DataFrame與Parquet
(1)保存DF為Parquet格式
dfPerson.write.parquet("person.parquet")
(2)hive中建立parquet格式的表
createtableperson_parquetlikepersonstoredasparquet;
insertoverwritetableperson_parquetselect*fromperson;
(3)加載Parquet文件不再需要case class。
valpersonDF =spark.read.parquet("person.parquet")
personDF.registerAsTempTable("pp")
valmales = spark.sql("select * from pp where gender='M'")
males.show
1.8.5 parquet文件的持久化
1.8.5.1 spark中將DataFrame數據寫到hdfs中的parquet文件中,支持追加
personDf.write.mode(SaveMode.Append).save(“hdfs://mapeng:9000/test/person.parquet”)
saveMode有如下幾種方式:
1.8.5.2 parquet文件合并
合并的規則:相同的列,在新的數據集中,是通用的列,
各自不同的列,也作為新的數據集的列。
實戰:
1.9 spark項目實戰