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集群的體系架構圖解:
2.Spark的主從結構
四.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
- 配置文件:conf/slave
mv slaves.template slaves
vi slaves
新增內容:
bigdata121
(5)啟動:
cd /opt/module/spark
sbin/start-all.sh
(6)驗證:192.168.127.121:8080
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
- 配置文件:conf/slave
mv slaves.template slaves
vi slaves
新增內容:
bigdata122
bigdata123
(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的集群整體架構如下圖所示。
配置參數 | 參考值 |
---|---|
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)另外:每個節點上,需要將以下兩行注釋掉。
(4)同步到其他兩臺服務器
(5)ZooKeeper中保存的信息
七.執行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")
參數說明:
- sc是SparkContext對象,該對象時提交spark程序的入口
- textFile("hdfs://192.168.88.111:9000/data/data.txt")是hdfs中讀取數據
- flatMap(_.split(" "))先map在壓平
- map((_,1))將單詞和1構成元組
- reduceByKey(+)按照key進行reduce,并將value累加
- saveAsTextFile("hdfs://192.168.88.111:9000/output/spark/wc")將結果寫入到hdfs中
(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版本
(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版本(直接輸出在屏幕)
(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執行的流程分析
2.Spark提交任務的流程析
九.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的類型:Transformation和Action
3.RDD的基本原理:
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"))
b.HDFS目錄:用于生產,集群模式
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的依據。
十一.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表示,通過頂點和邊來構建。