(十五)大數據學習之Spark

Spark

一.Spark生態圈:

(1)Spark Core : RDD(彈性分布式數據集)
(2)Spark SQL
(3)Spark Streaming
(4)Spark MLLib:協同過濾,ALS,邏輯回歸等等 --> 機器學習
(5)Spark Graphx : 圖計算

二.什么是Spark

1.Spark是什么:
Spark是一個針對大規模數據處理的快速通用引擎。

  • Spark是一種快速、通用、可擴展的大數據分析引擎,2009年誕生于加州大學伯克利分校AMPLab,2010年開源,2013年6月成為Apache孵化項目,2014年2月成為Apache頂級項目。目前,Spark生態系統已經發展成為一個包含多個子項目的集合,其中包含SparkSQL、Spark Streaming、GraphX、MLlib等子項目,Spark是基于內存計算的大數據并行計算框架。Spark基于內存計算,提高了在大數據環境下數據處理的實時性,同時保證了高容錯性和高可伸縮性,允許用戶將Spark部署在大量廉價硬件之上,形成集群。Spark得到了眾多大數據公司的支持,這些公司包括Hortonworks、IBM、Intel、Cloudera、MapR、Pivotal、百度、阿里、騰訊、京東、攜程、優酷土豆。當前百度的Spark已應用于鳳巢、大搜索、直達號、百度大數據等業務;阿里利用GraphX構建了大規模的圖計算和圖挖掘系統,實現了很多生產系統的推薦算法;騰訊Spark集群達到8000臺的規模,是當前已知的世界上最大的Spark集群。

2.特點:

  • 快:快100倍(Hadoop 3 之前)
  • 易用:支持多種語言開發
  • 通用性:生態系統全。
  • 易用性:兼容Hadoop

3.最大特點:基于內存

  • Spark是MapReduce的替代方案,而且兼容HDFS、Hive,可融入Hadoop的生態系統,以彌補MapReduce的不足。

三.Spark體系架構

1.Spark集群的體系架構圖解:

image.png

2.Spark的主從結構

image.png

四.Spark的安裝部署

1.Spark的安裝部署方式有以下幾種模式:
(1)Standalone: 本機調試

(2)YARN
(3)Mesos
(4)Amazon EC2
2.執行過程:
一個Worker有多個 Executor。 Executor是任務的執行者,按階段(stage)劃分任務。————> RDD

五.Spark的搭建:

1.準備工作

  • jdk
  • 配置主機名
  • 免密碼登錄

2.偽分布式模式安裝:
(1)下載
(2)上傳到linux
(3)解壓
(4)修改配置文件

  • 配置文件:conf/spark-env.sh
cd /opt/module

mv spark-2.1.0-bin-hadoop2.7/ spark/       //重命名spark文件夾

cd /opt/module/spark/conf 

mv spark-env.sh.template spark-env.sh    //重命名配置文件

vi spark-env.sh

修改內容如下:

export JAVA_HOME=/opt/module/jdk1.8.0_144
export SPARK_MASTER_HOST=bigdata121      //主節點的服務器名
export SPARK_MASTER_PORT=7077           //主節點端口號
//下面的可以不寫,默認
export SPARK_WORKER_CORES=1
export SPARK_WORKER_MEMORY=1024m
image.png
  • 配置文件:conf/slave
mv slaves.template slaves

vi slaves

新增內容:

bigdata121
image.png

(5)啟動:

cd /opt/module/spark

sbin/start-all.sh
image.png

(6)驗證:192.168.127.121:8080

image.png

3.全分布的安裝部署:
(1)下載
(2)上傳到linux
(3)解壓
(4)修改配置文件

  • 配置文件:conf/spark-env.sh
cd /opt/module

mv spark-2.1.0-bin-hadoop2.7/ spark/       //重命名spark文件夾

cd /opt/module/spark/conf 

mv spark-env.sh.template spark-env.sh    //重命名配置文件

vi spark-env.sh

修改內容如下:

export JAVA_HOME=/opt/module/jdk1.8.0_144
export SPARK_MASTER_HOST=bigdata121      //主節點的服務器名
export SPARK_MASTER_PORT=7077           //主節點端口號
//下面的可以不寫,默認
export SPARK_WORKER_CORES=1
export SPARK_WORKER_MEMORY=1024m
image.png
  • 配置文件:conf/slave
mv slaves.template slaves

vi slaves

新增內容:

bigdata122
bigdata123
image.png

(5)拷貝到其他兩臺服務器

cd /opt/module


src -r spark/ bigdata122:/opt/module

src -r spark/ bigdata123:/opt/module
  

(6)啟動Spark集群:

cd /opt/module/spark

sbin/start-all.sh

六.Spark的HA

1.回顧HA:
(1)HDFS,Yarn,Hbase,Spark:都是主從結構
(2)單點故障
2.基于文件系統的單點恢復
(1)主要用于開發或測試環境。當spark提供目錄保存spark Application和worker的注冊信息,并將他們的恢復狀態寫入該目錄中,這時,一旦Master發生故障,就可以通過重新啟動Master進程(sbin/start-master.sh),恢復已運行的spark Application和worker的注冊信息。
(2)基于文件系統的單點恢復,主要是在spark-en.sh里對SPARK_DAEMON_JAVA_OPTS設置

配置參數 參考值
spark.deploy.recoveryMode 設置為FILESYSTEM開啟單點恢復功能,默認值:NONE
spark.deploy.recoveryDirectory Spark 保存恢復狀態的目錄

3.基于Zookeeper的Standby Masters
(1)ZooKeeper提供了一個Leader Election機制,利用這個機制可以保證雖然集群存在多個Master,但是只有一個是Active的,其他的都是Standby。當Active的Master出現故障時,另外的一個Standby Master會被選舉出來。由于集群的信息,包括Worker, Driver和Application的信息都已經持久化到ZooKeeper,因此在切換的過程中只會影響新Job的提交,對于正在進行的Job沒有任何的影響。加入ZooKeeper的集群整體架構如下圖所示。

image.png
配置參數 參考值
spark.deploy.recoveryMode 設置為ZOOKEEPER開啟單點恢復功能,默認值:NONE
spark.deploy.zookeeper.url ZooKeeper集群的地址
spark.deploy.zookeeper.dir Spark信息在ZK中的保存目錄,默認:/spark

(2)修改spark-env.sh參考:

export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER 
-Dspark.deploy.zookeeper.url=bigdata12:2181,bigdata13:2181,bigdata14:2181 
-Dspark.deploy.zookeeper.dir=/spark"

(3)另外:每個節點上,需要將以下兩行注釋掉。

image.png

(4)同步到其他兩臺服務器
(5)ZooKeeper中保存的信息

image.png
image.png

七.執行Spark Demo程序

1.使用Spark Shell
(1)spark-shell是Spark自帶的交互式Shell程序,方便用戶進行交互式編程,用戶可以在該命令行下用scala編寫spark程序。相當于REPL ,作為一個獨立的Application運行
(2)兩種模式:

  • 本地模式:spark-shell 不接任何參數,代表本地模式
  • 集群模式:spark-shell后面帶有參數

(3)啟動Spark shell:

spark-shell

參數說明:

--master spark://spark81:7077     //指定Master的地址
--executor-memory 2g      //指定每個worker可用內存為2G
--total-executor-cores 2       //指定整個集群使用的cup核數為2個

例如:

spark-shell --master spark://spark81:7077 --executor-memory 2g --total-executor-cores 2

(4)在Spark shell中編寫WordCount程序

sc.textFile("hdfs://192.168.88.111:9000/data/data.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).saveAsTextFile("hdfs://192.168.88.111:9000/output/spark/wc")

參數說明:

(5)wordcount程序,處理本地文件,把結果打印到屏幕上

scala> sc.textFile("/usr/local/tmp_files/test_WordCount.txt")
                .flatMap(_.split(" "))
                .map((_,1))
                .reduceByKey(_+_)
                .collect
                
                res0: Array[(String, Int)] = Array((is,1), (love,2), (capital,1), (Beijing,2), (China,2), (I,2), (of,1), (the,1))

(6)wordcount程序,處理HDFS文件,結果保存在hdfs上

sc.textFile("hdfs://node1:8020/tmp_files/test_WordCount.txt")
                .flatMap(_.split(" "))
                .map((_,1))
                .reduceByKey(_+_)
                .saveAsTextFile("hdfs://node1:8020/output/0331/test_WordCount")

(7)單步運行wordcount --->RDD

scala> val rdd1 = sc.textFile("/usr/local/tmp_files/test_WordCount.txt")
                rdd1: org.apache.spark.rdd.RDD[String] = /usr/local/tmp_files/test_WordCount.txt MapPartitionsRDD[12] at textFile at <console>:24

scala> 1+1
res2: Int = 2

scala> rdd1.collect
res3: Array[String] = Array(I love Beijing, I love China, Beijing is the capital of China)

scala> val rdd2 = rdd1.flatMap(_.split(" "))
                rdd2: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[13] at flatMap at <console>:26

scala> rdd2.collect
res4: Array[String] = Array(I, love, Beijing, I, love, China, Beijing, is, the, capital, of, China)

scala> val rdd3 = rdd2.map((_,1))
                rdd3: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[14] at map at <console>:28

scala> rdd3.collect
res5: Array[(String, Int)] = Array((I,1), (love,1), (Beijing,1), (I,1), (love,1), (China,1), (Beijing,1), (is,1), (the,1), (capital,1), (of,1), (China,1))

scala> val rdd4 = rdd3.reduceByKey(_+_)
                rdd4: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[15] at reduceByKey at <console>:30

scala> rdd4.collect
res6: Array[(String, Int)] = Array((is,1), (love,2), (capital,1), (Beijing,2), (China,2), (I,2), (of,1), (the,1))

(8)RDD 彈性分布式數據集
(9)Scala復習:

  • flatten:把嵌套的結果展開
scala>List(List(2,4,6,8,10),List(1,3,5,7,9)).flatten
res21: List[Int] = List(2, 4, 6, 8, 10, 1, 3, 5, 7, 9)
  • flatmap:相當于一個 map + flatten
scala> var myList = List(List(2,4,6,8,10),List(1,3,5,7,9))
                    myList: List[List[Int]] = List(List(2, 4, 6, 8, 10), List(1, 3, 5, 7, 9))
scala> myList.flatMap(x=>x.map(_*2))
res22: List[Int] = List(4, 8, 12, 16, 20, 2, 6, 10, 14, 18)                 
myList.flatMap(x=>x.map(_*2))

flatmao執行過程:

  • 將 List(2, 4, 6, 8, 10), List(1, 3, 5, 7, 9) 調用 map(_*2) 方法。x 代表一個List
  • flatten
    2.在IDEA中編寫WordCount程序
    (1)需要的jar包:$SPARK_HOME/jars/*.jar
    (2)創建Scala Project,并創建Scala Object、或者Java Class
    (3)書寫源代碼,并打成jar包,上傳到Linux

Scala版本

image.png

(4)運行程序:

spark-submit --master spark://spark81:7077 
--class mydemo.WordCount jars/wc.jar 
hdfs://192.168.88.111:9000/data/data.txt 
hdfs://192.168.88.111:9000/output/spark/wc1

Java版本(直接輸出在屏幕)

image.png

(4)運行程序:

spark-submit --master spark://spark81:7077 
--class mydemo.JavaWordCount jars/wc.jar 
hdfs://192.168.88.111:9000/data/data.txt

八.Spark運行機制及原理分析

1.WordCount執行的流程分析

image.png

2.Spark提交任務的流程析

image.png

九.RDD和RDD特性,RDD的算子

1.RDD:彈性分布式數據集
(1)什么是RDD?

  • RDD(Resilient Distributed Dataset)叫做彈性分布式數據集,是Spark中最基本的數據抽象,它代表一個不可變、可分區、里面的元素可并行計算的集合。RDD具有數據流模型的特點:自動容錯、位置感知性調度和可伸縮性。RDD允許用戶在執行多個查詢時顯式地將工作集緩存在內存中,后續的查詢能夠重用工作集,這極大地提升了查詢速度。

(2)RDD的屬性:

  • 一組分片(Partition),即數據集的基本組成單位。對于RDD來說,每個分片都會被一個計算任務處理,并決定并行計算的粒度。用戶可以在創建RDD時指定RDD的分片個數,如果沒有指定,那么就會采用默認值。默認值就是程序所分配到的CPU Core的數目。
  • 一個計算每個分區的函數。Spark中RDD的計算是以分片為單位的,每個RDD都會實現compute函數以達到這個目的。compute函數會對迭代器進行復合,不需要保存每次計算的結果。
  • RDD之間的依賴關系。RDD的每次轉換都會生成一個新的RDD,所以RDD之間就會形成類似于流水線一樣的前后依賴關系。在部分分區數據丟失時,Spark可以通過這個依賴關系重新計算丟失的分區數據,而不是對RDD的所有分區進行重新計算。
  • 一個Partitioner,即RDD的分片函數。當前Spark中實現了兩種類型的分片函數,一個是基于哈希的HashPartitioner,另外一個是基于范圍的RangePartitioner。只有對于于key-value的RDD,才會有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函數不但決定了RDD本身的分片數量,也決定了parent RDD Shuffle輸出時的分片數量。
  • 一個列表,存儲存取每個Partition的優先位置(preferred location)。對于一個HDFS文件來說,這個列表保存的就是每個Partition所在的塊的位置。按照“移動數據不如移動計算”的理念,Spark在進行任務調度的時候,會盡可能地將計算任務分配到其所要處理數據塊的存儲位置。

2.如何創建RDD
(1)通過SparkContext.parallelize方法來創建(通過sc.parallelize進行創建)

scala> val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8),3)
rdd1:org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[32] at parallelize at <console>:29
            
scala> rdd1.partitions.length
res35: Int = 3
scala> val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8),2)
rdd1:org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[33] at parallelize at <console>:29

scala> rdd1.partitions.length
res36: Int = 2

(2)通過外部數據源來創建

sc.textFile()           
scala> val rdd2 = sc.textFile("/usr/local/tmp_files/test_WordCount.txt")
rdd2:org.apache.spark.rdd.RDD[String] = /usr/local/tmp_files/test_WordCount.txt MapPartitionsRDD[35] at textFile at <console>:29

(3)RDD的類型:TransformationAction
3.RDD的基本原理:

image.png

4.Transformation
(1)RDD中的所有轉換都是延遲加載的,也就是說,它們并不會直接計算結果。相反的,它們只是記住這些應用到基礎數據集(例如一個文件)上的轉換動作。只有當發生一個要求返回結果給Driver的動作時,這些轉換才會真正運行。這種設計讓Spark更加有效率地運行。

轉換 含義
map(func) 返回一個新的RDD,該RDD由每一個輸入元素經過func函數轉換后組成
filter(func) 返回一個新的RDD,該RDD由經過func函數計算后返回值為true的輸入元素組成
flatMap(func) 類似于map,但是每一個輸入元素可以被映射為0或多個輸出元素(所以func應該返回一個序列,而不是單一元素)
mapPartitions(func) 類似于map,但獨立地在RDD的每一個分片上運行,因此在類型為T的RDD上運行時,func的函數類型必須是Iterator[T] => Iterator[U]
mapPartitionsWithIndex(func) 類似于mapPartitions,但func帶有一個整數參數表示分片的索引值,因此在類型為T的RDD上運行時,func的函數類型必須是(Int, Interator[T]) => Iterator[U]
sample(withReplacement, fraction, seed) 根據fraction指定的比例對數據進行采樣,可以選擇是否使用隨機數進行替換,seed用于指定隨機數生成器種子
union(otherDataset) 對源RDD和參數RDD求并集后返回一個新的RDD
intersection(otherDataset) 對源RDD和參數RDD求交集后返回一個新的RDD
distinct([numTasks])) 對源RDD進行去重后返回一個新的RDD
groupByKey([numTasks]) 在一個(K,V)的RDD上調用,返回一個(K, Iterator[V])的RDD
reduceByKey(func, [numTasks]) 在一個(K,V)的RDD上調用,返回一個(K,V)的RDD,使用指定的reduce函數,將相同key的值聚合到一起,與groupByKey類似,reduce任務的個數可以通過第二個可選的參數來設置
aggregateByKey(zeroValue)(seqOp,combOp,[numTasks])
sortByKey([ascending], [numTasks]) 在一個(K,V)的RDD上調用,K必須實現Ordered接口,返回一個按照key進行排序的(K,V)的RDD
sortBy(func,[ascending], [numTasks]) 與sortByKey類似,但是更靈活
join(otherDataset, [numTasks]) 在類型為(K,V)和(K,W)的RDD上調用,返回一個相同key對應的所有元素對在一起的(K,(V,W))的RDD
cogroup(otherDataset, [numTasks]) 在類型為(K,V)和(K,W)的RDD上調用,返回一個(K,(Iterable<V>,Iterable<W>))類型的RDD
cartesian(otherDataset) 笛卡爾積
pipe(command, [envVars])
coalesce(numPartitions)
repartition(numPartitions)
repartitionAndSortWithinPartitions(partitioner)

5.Action

動作 含義
reduce(func) 通過func函數聚集RDD中的所有元素,這個功能必須是課交換且可并聯的
collect() 在驅動程序中,以數組的形式返回數據集的所有元素
count() 返回RDD的元素個數
first() 返回RDD的第一個元素(類似于take(1))
take(n) 返回一個由數據集的前n個元素組成的數組
takeSample(withReplacement,num, [seed]) 返回一個數組,該數組由從數據集中隨機采樣的num個元素組成,可以選擇是否用隨機數替換不足的部分,seed用于指定隨機數生成器種子
takeOrdered(n, [ordering])
saveAsTextFile(path) 將數據集的元素以textfile的形式保存到HDFS文件系統或者其他支持的文件系統,對于每個元素,Spark將會調用toString方法,將它裝換為文件中的文本
saveAsSequenceFile(path) 將數據集中的元素以Hadoop sequencefile的格式保存到指定的目錄下,可以使HDFS或者其他Hadoop支持的文件系統。
saveAsObjectFile(path)
countByKey() 針對(K,V)類型的RDD,返回一個(K,Int)的map,表示每一個key對應的元素個數。
foreach(func) 在數據集的每一個元素上,運行函數func進行更新。

十.RDD特性

1.RDD的緩存機制:
(1)作用:提高性能
(2)使用:標識RDD可以被緩存 persist cache
(3)可以緩存的位置:

2.RDD的容錯機制:通過檢查點來實現
(1)
(1)復習檢查點:HDFS中的檢查點:有SecondaryNamenode來實現日志的合并。
(2)RDD的檢查點:容錯

  • 概念:血統 Lineage
  • 理解:表示任務執行的生命周期。
  • WordCount textFile ---> redceByKey
  • 如果血統越長,越容易出錯。
  • 假如有檢查點,可以從最近的一個檢查點開始,往后面計算。不用重頭計算。

(3)RDD檢查點的類型:
a.基于本地目錄:需要將Spark shell 或者任務運行在本地模式上(setMaster("local"))

image.png

b.HDFS目錄:用于生產,集群模式

image.png
sc.setCheckPointDir(目錄)
                
//舉例:設置檢查點
scala> var rdd1 = sc.textFile("hdfs://192.168.109.131:8020/tmp_files/test_Cache.txt")
rdd1: org.apache.spark.rdd.RDD[String] = hdfs://192.168.109.131:8020/tmp_files/test_Cache.txt MapPartitionsRDD[1] at textFile at <console>:24

//設置檢查點目錄:
scala> sc.setCheckpointDir("hdfs://192.168.109.131:8020/sparkckpt")

//標識rdd1可以執行檢查點操作
scala> rdd1.checkpoint

scala> rdd1.count
res2: Long = 923452 

3.依賴關系:寬依賴,窄依賴
(1)RDD的依賴關系:

  • RDD和它依賴的父RDD(s)的關系有兩種不同的類型,即窄依賴(narrow dependency)和寬依賴(wide dependency)。
    (2)窄依賴指的是每一個父RDD的Partition最多被子RDD的一個Partition使用
    總結:窄依賴我們形象的比喻為獨生子女

(3)寬依賴指的是多個子RDD的Partition會依賴同一個父RDD的Partition
總結:寬依賴我們形象的比喻為超生

4.Spark任務中的Stage

  • DAG(Directed Acyclic Graph)叫做有向無環圖,原始的RDD通過一系列的轉換就就形成了DAG,根據RDD之間的依賴關系的不同將DAG劃分成不同的Stage,對于窄依賴,partition的轉換處理在Stage中完成計算。對于寬依賴,由于有Shuffle的存在,只能在parent RDD處理完成后,才能開始接下來的計算,因此寬依賴是劃分Stage的依據。
image.png

十一.RDD的高級算子

1.mapPartitionsWithIndex:對RDD中的每個分區(帶有下標)進行操作,下標用index表示
通過這個算子,我們可以獲取分區號。

def mapPartitionsWithIndex[U](
        f: (Int, Iterator[T]) ? Iterator[U], 
        preservesPartitioning: Boolean = false)(
        implicit arg0: ClassTag[U]): RDD[U]
        
//參數:f是個函數參數 f 中第一個參數是Int,代表分區號,第二個Iterator[T]代表分區中的元素

例如:

scala> val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8),3)
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[3] at parallelize at <console>:24

scala> def fun1(index:Int, iter:Iterator[Int]) : Iterator[String] = {
             | iter.toList.map(x => "[partId : " + index + " , value = " + x + " ]").iterator
             | }
fun1: (index: Int, iter: Iterator[Int])Iterator[String]

scala> rdd1.mapPartitions
        mapPartitions   mapPartitionsWithIndex

scala> rdd1.mapPartitionsWithIndex(fun1).collect
res3: Array[String] = Array(
        [partId : 0 , value = 1 ], [partId : 0 , value = 2 ], [partId : 1 , value = 3 ], [partId : 1 , value = 4 ], [partId : 1 , value = 5 ], [partId : 2 , value = 6 ], [partId : 2 , value = 7 ], [partId : 2 , value = 8 ])

2.aggregate:聚合操作。類似于分組。
(1)先對局部進行聚合操作,再對全局進行聚合操作。

//調用聚合操作
scala> val rdd2 = sc.parallelize(List(1,2,3,4,5),2)
rdd2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[5] at parallelize at <console>:24

scala> rdd2.mapPartitionsWithIndex(fun1).collect
res4: Array[String] = Array(
        [partId : 0 , value = 1 ], [partId : 0 , value = 2 ], [partId : 1 , value = 3 ], [partId : 1 , value = 4 ], [partId : 1 , value = 5 ])

scala> import scala.math._
import scala.math._

scala> rdd2.aggregate(0)(max(_,_),_+_)
res6: Int = 7

說明:
(2)對字符串操作

scala> val rdd2 = sc.parallelize(List("a","b","c","d","e","f"),2)
rdd2: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[7] at parallelize at <console>:27

scala> rdd2.aggregate("")(_+_,_+_)
res11: String = abcdef

scala> rdd2.aggregate("*")(_+_,_+_)
res12: String = **def*abc

(3)復雜的例子:
a.

scala> val rdd3 = sc.parallelize(List("12","23","345","4567"),2)
rdd3: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[8] at parallelize at <console>:27
        
scala> def fun1(index:Int, iter:Iterator[String]) : Iterator[String] = {
             | iter.toList.map(x => "[partId : " + index + " , value = " + x + " ]").iterator
             | }

scala> rdd3.mapPartitionsWithIndex(fun1).collect
res17: Array[String] = Array(
        [partId : 0 , value = 12 ], [partId : 0 , value = 23 ], [partId : 1 , value = 345 ], [partId : 1 , value = 4567 ])
        
scala> rdd3.aggregate("")((x,y)=> math.max(x.length,y.length).toString,(x,y)=>x+y)
res13: String = 42

執行過程:

  • 第一個分區:
    (a)第一次比較: "" "12" 長度最大值 2 2-->"2"
    (b)第二次比較: “2” “23” 長度最大值 2 2-->"2"

  • 第二個分區:
    (a)第一次比較: "" "345" 長度最大值 3 3-->"3"
    (b)第二次比較: “3” “4567” 長度最大值 4 4-->"4"

b.

rdd3.aggregate("")((x,y)=> math.min(x.length,y.length).toString,(x,y)=>x+y)
        
scala> rdd3.aggregate("")((x,y)=> math.min(x.length,y.length).toString,(x,y)=>x+y)
res18: String = 11

執行過程:

  • 第一個分區:
    第一次比較: "" "12" 長度最小值 0 0-->"0"
    第二次比較: “0” “23” 長度最小值 1 1-->"1"

  • 第二個分區:
    第一次比較: "" "345" 長度最小值 0 0-->"0"
    第二次比較: “0” “4567” 長度最小值 1 1-->"1"
    c.aggregateByKey:類似于aggregate,區別:操作的是 key value 的數據類型。

3.其他高級算子:

十二.編程案例

1.分析日志
(1)需求:找到訪問量最高的兩個網頁

  • 第一步:對網頁的訪問量求和
  • 第二步:排序,降序

(2)創建自定義分區

(3)使用JDBCRDD 操作數據庫

(4)操作數據庫:把結果存放到數據庫中

Spark SQL

Spark sql基礎

1.什么是Spark SQL

  • Spark SQL 是spark的一個模塊。來處理 結構化 的數據,不能處理非結構化的數據
    2.特點:
    (1)容易集成:
  • 不需要單獨安裝。

(2)統一的數據訪問方式

  • 結構化數據的類型:JDBC JSon Hive parquer文件 都可以作為Spark SQL 的數據源
  • 對接多種數據源,且使用方式類似
    (3)完全兼容hive
  • 把Hive中的數據,讀取到Spark SQL中運行。

(4) 支持標準的數據連接

  • JDBC

3.為什么學習Spark SQL

  • 執行效率比Hive高
  • hive 2.x 執行引擎可以使用 Spark
    4.核心概念:表(DataFrame DataSet)
  • mysql中的表:表結構、數據
  • DataFrame:Schema、RDD(數據)
  • DataSet 在spark1.6以后,對DataFrame做了一個封裝。
    5.創建DataFrame
    測試數據:員工表、部門表
    (1)第一種方式:使用case class
    a.定義Schema
    b.讀取文件
    c.把每行數據,映射到Emp上
    d.生成DataFrame

(2)第二種方式 使用Spark Session
(3)直接讀取一個帶格式的文件。

6.操作DataFrame
(1)DSL語句
(2)SQL語句
注意:不能直接執行SQL,需要生成一個視圖,再執行sql。
(3)多表查詢

7.操作DataSet
(1)跟DataFrame類似,是一套新的接口。高級的Dataframe
(2)創建DataSet

  • 使用序列來創建DataSet。
  • 使用JSON數據來創建DataSet
  • 使用其他數據

(3)DataSet案例

(4)多表查詢

  • 創建部門表
  • 創建員工表
  • 執行多表查詢:等值連接
  • 多表連接后再篩選

7.Spark SQL中的視圖
(1)視圖是一個虛表,不存儲數據。
(2)兩種類型:

  • 普通視圖(本地視圖):只在當前Session中有效。createOrReplaceTempView createTempView
  • 全局視圖: createGlobalTempView:在不同的Session中都有用 把全局視圖創建在命名空間中:global_temp中。類似于一個庫。

二.使用數據源

1.在Spark SQL中,可以使用各種各樣的數據源來操作。 結構化
2.使用load函數、save函數

  • load函數是加載數據,save是存儲數據
    注意:使用load 或 save時,默認是Parquet文件。列式存儲文件。
    3.Parquet文件:列式存儲文件,是Spark SQL 默認的數據源
  • 就是一個普通的文件

(1)把其他文件,轉換成Parquet文件
(2)支持Schema的合并

4.json文件

5.JDBC
(1)使用JDBC操作關系型數據庫,加載到Spark中進行分析和處理。
(2)方式一:
(3)方式二:

6.使用hive
(1)spark SQL 完全兼容hive
(2)需要進行配置

  • 拷貝一下文件到spark/conf目錄下:
  • Hive 配置文件: hive-site.xml
  • Hadoop 配置文件:core-site.xml hdfs-site.xml

(3)配置好后,重啟spark

(4)啟動Hadoop 與 hive

三.在IDE中開發Spark SQL

四.性能優化

1.用內存中緩存表的數據
直接讀取內存的值,來提高性能
2.了解性能優化的相關參數:參考講義

Spark Streaming

一.常用的實時計算引擎(流式計算)

1.Apache Storm:真正的流式計算

2.Spark Streaming :嚴格上來說,不是真正的流式計算(實時計算)
把連續的流式數據,當成不連續的RDD
本質:是一個離散計算(不連續)
3.Apache Flink:真正的流式計算。與Spark Streaming相反。
把離散的數據,當成流式數據來處理
4.JStorm

二.Spark Streaming基礎

1.什么是 Spark Streaming。

  • Spark Streaming makes it easy to build scalable fault-tolerant streaming applications.
    易于構建靈活的、高容錯的流式系統。

2.特點:

  • 易用,已經集成到Spark中
  • 容錯性:底層RDD,RDD本身具有容錯機制
  • 支持多種語言:Java Scala Python

3.演示官方的Demo
往Spark Streaming中發送字符串,Spark 接收到以后,進行計數
使用消息服務器 netcat Linux自帶
yum install nc.x86_64
nc -l 1234
注意:總核心數 大于等于2。一個核心用于接收數據,另一個用于處理數據
在netcat中寫入數據 Spark Streaming可以取到

4.開發自己的NetWorkWordCount程序,和Spark Core類似
問題:Hello Hello
Hello World
現在現象:(Hello,2)
(Hello , 1) (World , 1)
能不能累加起來?保存記錄下以前的狀態?
通過Spark Streaming提供的算子來實現

三.高級特性:

1.什么是DStream?離散流

  • 把連續的數據變成不連續的RDD
  • 因為DStream的特性,導致,Spark Streaming不是真正的流式計算

2.重點算子講解
(1)updateStateByKey
默認情況下,Spark Streaming不記錄之前的狀態,每次發數據,都會從0開始
現在使用本算子,實現累加操作。
(2)transform

3.窗口操作

  • 窗口:對落在窗口內的數據進行處理,也是一個DStream,RDD
  • 舉例:每10秒鐘把過去30秒的數據采集過來
  • 注意:先啟動nc 再啟動程序 local[2]

4.集成Spark SQL : 使用SQL語句來處理流式數據
5.緩存和持久化:和RDD一樣
6.支持檢查點:和RDD一樣

四.數據源

Spark Streaming是一個流式計算引擎,就需要從外部數據源來接收數據
1.基本的數據源

  • 文件流:監控文件系統的變化,如果文件有增加,讀取文件中的內容
    希望Spark Streaming監控一個文件夾,如果有變化,則把變化采集過來
  • RDD隊列流:可以從隊列中獲取數據
  • 套接字流:socketTextStream

2.高級數據源
(1)Flume
(2)Spark SQL 對接flume有多種方式:

  • push方式:flume將數據推送給Spark Streaming
  • custom sink 模式:比第一種有更好的健壯性和容錯性。使用這種方式,flume配置一個sink。
  • 使用官方提供的spark sink組件
    需要把 spark-streaming-flume-sink_2.10-2.1.0.jar 拷貝到flume lib下
    需要把 spark-streaming-flume-sink_2.10-2.1.0.jar 拷貝到IDE的lib下添加到build path中
(3)Kafka
    在講Kafka時,舉例。

四.性能優化的參數

(1)性能優化:
spark submit的時候,程序報OOM錯誤
程序跑的很慢
(2)方法:調整spark參數
conf.set...

性能調優

一.Spark 性能優化概覽:

  • Spark的計算本質是,分布式計算。
  • 所以,Spark程序的性能可能因為集群中的任何因素出現瓶頸:CPU、網絡帶寬、或者內存。
  • CPU、網絡帶寬,是運維來維護的。
  • 聚焦點:內存。
  • 如果內存能夠容納下所有的數據,那就不需要調優了。
  • 如果內存比較緊張,不足以放下所有數據(10億量級---500G),需要對內存的使用進行性能優化。
  • 比如:使用某些方法減少內存的消耗。

二.Spark性能優化,主要針對在內存的使用調優。

三.Spark性能優化的技術:

1.使用高性能序列化類庫
2.優化數據結構
3.對于多次使用的RDD進行持久化、checkpoint
4.持久化級別:MEMORY_ONLY ---> MEMORY_ONLY_SER 序列化
5.Java虛擬機垃圾回收調優
6.Shuffle調優,1.x版本中,90%的性能問題,都是由于Shuffle導致的。

四.其他性能優化:

1.提高并行度
2.廣播共享數據
等等。。。

五.診斷Spark內存使用:首先要看到內存使用情況,才能進行針對性的優化。

1.內存花費:
(1)每個Java對象,都有一個對象頭,占用16字節,包含一些對象的元信息,比如指向他的類的指針。

  • 如果對象本身很小,比如int,但是他的對象頭比對象自己還大。

(2)Java的String對象,會比他內存的原始數據,多出40個字節。

  • String內部使用的char數組來保存內部的字符串序列,并且還要保存諸如輸出長度之類的信息。
  • char使用的是UTF-16編碼,每個字符會占2個字節。比如,包含10個字符的String,2*10+40=60字節
    (3)Java中的集合類型,比如HashMap和LinkedList,內部使用鏈表數據結構。
  • 鏈表中的每個數據,使用Entry對象包裝。
  • Entry對象,不光有對象頭,還有指向下一個Entry的指針,占用8字節。

(4)元素類型為原始數據類型(int),內部通常會使用原始數據類型的包裝類型(Integer)來存儲元素。
2.如何判斷Spark程序消耗內存情況?:答案是預估
(1)設置RDD的并行度。

  • 兩種方法創建RDD,parallelize() textFile() 在這兩個方法中,傳入第二個參數,設置RDD的partition數量。
  • 在SparkConfig中設置一個參數:
  • spark.default.parallelism
  • 可以統一設置這個application中所有RDD的partition數量

(2)將RDD緩存 cache()

(3)觀察日志:driver日志

/usr/local/spark-2.1.0-bin-hadoop2.7/work
            19/04/13 22:01:05 INFO MemoryStore: Block rdd_3_1 stored as values in memory (estimated size 26.0 MB, free 339.9 MB)
            19/04/13 22:01:06 INFO MemoryStore: Block rdd_3_0 stored as values in memory (estimated size 26.7 MB, free 313.2 MB)

(4)將這個內存信息相加,就是RDD內存占用量。

六.使用高性能序列化類庫

1.數據序列化概述

  • 數據序列化,就是將對象或者數據結構,轉換成特定的格式,使其可在網絡中傳輸,或存儲在內存或文件中。

  • 反序列化,是相反的操作,將對象從序列化數據中還原出來。

  • 序列化后的數據格式,可以是二進制,xml,Json等任何格式。

  • 對象、數據序列化的重點在于數據的交換與傳輸。

  • 在任何分布式系統中,序列化都是扮演著一個重要的角色。

  • 如果使用的序列化技術,操作很慢,或者序列化后的數據量還是很大,會讓分布式系統應用程序性能下降很多。

  • 所以,Spark性能優化的第一步,就是進行序列化的性能優化。

  • Spark自身默認會在一些地方對數據進行序列化,比如Shuffle。另外,我們使用了外部數據(自定義類型),也要讓其課序列化。

  • Spark本身對序列化的便捷性和性能進行了取舍

  • 默認情況下:Spark傾向于序列化的便捷性,使用了Java自身提供的序列化機制,很方便使用。

  • 但是,Java序列化機制性能不高,序列化速度慢,序列化后數據較大,比較占用內存空間。

2.kryo

  • Spark支持使用kryo類庫來進行序列化。
  • 速度快,占用空間更小,比Java序列化數據占用空間小10倍。
    3.如何使用kryo序列化機制
    (1)設置Spark conf
bin/spark-submit will also read configuration options from conf/spark-defaults.conf, 
in which each line consists of a key and a value separated by whitespace. For example:

spark.master            spark://5.6.7.8:7077
spark.executor.memory   4g
spark.eventLog.enabled  true
spark.serializer   org.apache.spark.serializer.KryoSerializer

(2)使用kryo是,要求需要序列化的類,要提前注冊,以獲得高性能

4.kryo類庫的優化
(1)優化緩存大小

  • 如果注冊的自定義類型,本身特別大(100個字段),會導致要序列化的對象太大。此時需要對kyro本身進行優化。因為kryo內部的緩存,可能不能存放這么大的class對象。
spark.kryoserializer.buffer.max  //設置這個參數,將其調大。

(2)預先注冊自定義類型

  • 雖然不注冊自定義類型,kryo也可以正常工作,但會保存一份他的全限定類名,耗費內存。
  • 推薦預先注冊要序列化的自定義類型。

七.優化數據結構

1.概述

  • 要減少內存的消耗,除了使用高效的序列化類庫外,還要優化數據結構。
  • 避免Java語法特性中所導致的額外內存開銷。
  • 核心:優化算子函數內部使用到的局部數據或算子函數外部的數據。
  • 目的:減少對內存的消耗和占用。

2.如何做?
(1)優先使用數組以及字符串,而不是集合類。即:優先使用Array,而不是ArrayList、LinkedList、HashMap

  • 使用int[] 會比List<Integer> 節省內存
    (2)將對象轉換成字符串。
  • 企業中,將HashMap、List這種數據,統一用String拼接成特殊格式的字符串
Map<Integer,Person> persons = new HashMap<Integer,Person>()
可以優化為:
"id:name,address"
String persons = "1:Andy,Beijing|2:Tom,Tianjin...."

(3)避免使用多層嵌套對象結構
(4)對于能夠避免的場景,盡量使用int代替String

  • 雖然String比List效率高,但int類型占用更少內存,比如:數據庫主鍵,id,推薦使用自增的id,而不是uuid

八.rdd.cache checkpoint

九.持久化級別:MEMORY_ONLY ---> MEMORY_ONLY_SER 序列化

十.Java虛擬機的調優

1.概述

  • 如果在持久化RDD的時候,持久化了大量的數據,那么Java虛擬機的垃圾回收就可能成為一個瓶頸
  • Java虛擬機會定期進行垃圾回收,此時會追蹤所有Java對象,并且在垃圾回收時,找到那些已經不再使用的對象。
  • 清理舊對象,給新對象騰出空間。
  • 垃圾回收的性能開銷,是與內存中的對象數量成正比。
  • 在做Java虛擬機調優之前,必須先做好上面的調優工作,這樣才有意義。
  • 必須注意順序

2.Spark GC原理
見圖片

3.監測垃圾回收

  • 我們可以進行監測,比如多久進行一次垃圾回收以及耗費的時間等等。
spark-submit腳本中,添加一個配置
--conf "spark.executor.extraJavaOptions=-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimesStamps"

注意:這個是輸出到worker日志中,而不是driver日志。
/usr/local/spark-2.1.0-bin-hadoop2.7/logs  worker日志
/usr/local/spark-2.1.0-bin-hadoop2.7/work  driver日志

4.優化Executor內存比例
(1)目的:減少GC次數。

  • 對于GC調優來說,最重要的就是調節,RDD的緩存占用的內存空間 與 算子執行時創建對象所占用的內存空間 的比例
  • 對于默認情況,Spark使用每個Executor 60% 的內存空間來緩存RDD,在task運行期間所創建的對象,只有40%內存空間來存放。
//使用:
conf.set("spark.storage.memoryFraction",0.5)

5.Java GC 調優 (-)

十一.shuffle原理

1.優化前
圖片
2.優化后
圖片

十二.其他調優

1.提高并行度
2.廣播共享數據

Spark Mllib:MLlib 是 Spark 可以擴展的機器學習庫。

一.MLlib概述

MLlib 是 Spark 可以擴展的機器學習庫。
Spark在機器學習方面具有得天獨厚的有事,有以下幾個原因:
1.機器學習算法一般都有多個步驟迭代計算,需要在多次迭代后,獲得足夠小的誤差或者收斂才會停止。

    double wucha = 1.0
    while(wucha>=0.00001){
        建模  wucha -= 某個值
    }
    
    模型計算完畢
    
    當迭代使用Hadoop的MapReduce計算框架時,每次都要讀寫硬盤以及任務啟動工作,導致很大的IO開銷。
    而Spark基于內存的計算模型天生擅長迭代計算。只有在必要時,才會讀寫硬盤。
    所以Spark是機器學習比較理想的平臺。

2.通信,Hadoop的MapReduce計算框架,通過heartbeat方式來進行通信和傳遞數據,執行速度慢。

  • spark 有高效的 Akka 和 Netty 的通信系統,通行效率高。
  • SPark MLlib 是Spark 對常用的機器學習算法的實現庫,同時包括相關測試和數據生成器。

二.什么是機器學習?

1.機器學習的定義。
A computer program is said to learn from experience E with respect to some class of tasks T and performance measure P,
if its performance at tasks in T, as measured by P, improves with experience E。

2.三個關鍵詞:算法、經驗、模型評價
在數據的基礎上,通過算法構建出模型,并進行評價
如果達到要求,則用該模型測試其他數據
如果不達到要求,要調整算法來重新建立模型,再次進行評估
循環往復,知道獲得滿意的經驗

3.應用:金融反欺詐、語音識別、自然語言處理、翻譯、模式識別、智能控制等等

4.基于大數據的機器學習
(1)傳統的機器學習算法,由于技術和單機存儲的現值,只能在少量數據上使用。即,依賴于數據抽樣。
(2)傳統的機器學習存在的問題:很難做好隨機,導致學習的模型不準確。
(3)在大數據上進行機器學習,直接處理全量數據并進行大量迭代計算。
(4)Spark本身計算優勢,適合機器學習。
(5)另外 spark-shell pyspark 都可以提供及時查詢工具

5.MLlib

  • MLlib是Spark機器學習庫,簡化機器學習的工程實踐工作,方便擴展到更大規模。

  • 集成了通用的學習算法:分類、回歸、聚類、協同過濾、降維等等

  • 另外,MLlib本身在Spark中,數據清洗、SQL、建模放在一起。

三、線性回歸

四、余弦相似性
https://blog.csdn.net/u012160689/article/details/15341303

Spark Graphx

一.Spark Graphx 是什么?

1.是Spark 的一個模塊,主要用于進行以圖為核心的計算,還有分布式圖計算
2.Graphx 底層基于RDD計算,和RDD共用一種存儲形態。在展示形態上,可以用數據集來表示,也可以用圖來表示。

二.Spark GraphX 有哪些抽象?

1.頂點
RDD[(VertexId,VD)]表示
VertexId 代表了頂點的ID,是Long類型
VD 是頂點的屬性,可以是任何類型
2.邊
RDD[Edge[ED]]表示
Edge表示一個邊
包含一個ED類型參數來設定屬性
另外,邊還包含了源頂點ID和目標頂點ID

3.三元組
三元組結構用RDD[EdgeTriplet[VD,ED]]表示
三元組包含一個邊、邊的屬性、源頂點ID、源頂點屬性、目標頂點ID、目標頂點屬性。

4.圖
Graph表示,通過頂點和邊來構建。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內容