spark 學習筆記

Spark學習筆記

Data

Source->Kafka->Spark Streaming->Parquet->Spark SQL(SparkSQL可以結合MLGraphX)->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

importspark.implicits._

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項目實戰

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容