廣播變量和累加器
廣播變量
- 廣播變量理解圖
- 廣播變量使用
val conf = new SparkConf()
conf.setMaster("local").setAppName("brocast")
val sc = new SparkContext(conf)
val list = List("hello xasxt")
val broadCast = sc.broadcast(list)
val lineRDD = sc.textFile("./words.txt")
lineRDD.filter { x => broadCast.value.contains(x) }.foreach { println}
sc.stop()
- 注意事項
- 能不能將一個RDD使用廣播變量廣播出去?
不能,因爲RDD是不存儲數據的。可以將RDD的結果廣播出去。 - 廣播變量只能在Driver端定義,不能在Executor端定義。
- 在Driver端可以修改廣播變量的值,在Executor端無法修改廣播變量的值。
累加器
- 累加器理解圖
- 累加器的使用
val conf = new SparkConf()
conf.setMaster("local").setAppName("accumulator")
val sc = new SparkContext(conf)
val accumulator = sc.accumulator(0)
sc.textFile("./words.txt").foreach { x =>{accumulator.add(1)}}
println(accumulator.value)
sc.stop()
- 注意事項
累加器在Driver端定義賦初始值,累加器只能在Driver端讀取,在Excutor端更新。
SparkUI
- SparkUI界面介紹
可以指定提交Application的名稱
./spark-shell --master spark://node1:7077 --name myapp
- 配置historyServer
臨時配置,對本次提交的應用程序起作用
./spark-shell --master spark://node1:7077
--name myapp1
--conf spark.eventLog.enabled=true
--conf spark.eventLog.dir=hdfs://node1:9000/spark/test
停止程序,在Web Ui中Completed Applications對應的ApplicationID中能查看history。
spark-default.conf配置文件中配置HistoryServer,對所有提交的Application都起作用
在客戶端節點,進入../spark-1.6.0/conf/ spark-defaults.conf最後加入:
//開啓記錄事件日誌的功能
spark.eventLog.enabled true
//設置事件日誌存儲的目錄
spark.eventLog.dir hdfs://node1:9000/spark/test
//設置HistoryServer加載事件日誌的位置
spark.history.fs.logDirectory hdfs://node1:9000/spark/test
//日誌優化選項,壓縮日誌
spark.eventLog.compress true
啓動HistoryServer
./start-history-server.sh
訪問HistoryServer:node4:18080,之後所有提交的應用程序運行狀況都會被記錄。
Master HA
- Master的高可用原理
Standalone集羣只有一個Master,如果Master掛了就無法提交應用程序,需要給Master進行高可用配置,Master的高可用可以使用fileSystem(文件系統)和zookeeper(分佈式協調服務)。
fileSystem只有存儲功能,可以存儲Master的元數據信息,用fileSystem搭建的Master高可用,在Master失敗時,需要我們手動啓動另外的備用Master,這種方式不推薦使用。
zookeeper有選舉和存儲功能,可以存儲Master的元素據信息,使用zookeeper搭建的Master高可用,當Master掛掉時,備用的Master會自動切換,推薦使用這種方式搭建Master的HA。
- Master高可用搭建
在Spark Master節點上配置主Master,配置spark-env.sh
export SPARK_DAEMON_JAVA_OPTS="
-Dspark.deploy.recoveryMode=ZOOKEEPER
-Dspark.deploy.zookeeper.url=node3:2181,node4:2181,node5:2181
-Dspark.deploy.zookeeper.dir=/sparkmaster0821"
發送到其他worker節點上
找一臺節點(非主Master節點)配置備用 Master,修改spark-env.sh配置節點上的MasterIP
啓動集羣之前啓動zookeeper集羣:
../zkServer.sh start
啓動spark Standalone集羣,啓動備用Master
打開主Master和備用Master WebUI頁面,觀察狀態。
- 注意點
主備切換過程中不能提交Application。
主備切換過程中不影響已經在集羣中運行的Application。因爲Spark是粗粒度資源調度。 - 測試驗證
提交SparkPi程序,kill主Master觀察現象。
./spark-submit
--master spark://node1:7077,node2:7077
--class org.apache.spark.examples.SparkPi
../lib/spark-examples-1.6.0-hadoop2.6.0.jar
10000
SparkShuffle
- SparkShuffle概念
reduceByKey會將上一個RDD中的每一個key對應的所有value聚合成一個value,然後生成一個新的RDD,元素類型是<key,value>對的形式,這樣每一個key對應一個聚合起來的value。
問題:聚合之前,每一個key對應的value不一定都是在一個partition中,也不太可能在同一個節點上,因爲RDD是分佈式的彈性的數據集,RDD的partition極有可能分佈在各個節點上。
如何聚合?- Shuffle Write:上一個stage的每個map task就必須保證將自己處理的當前分區的數據相同的key寫入一個分區文件中,可能會寫入多個不同的分區文件中。
- Shuffle Read:reduce task就會從上一個stage的所有task所在的機器上尋找屬於己的那些分區文件,這樣就可以保證每一個key所對應的value都會匯聚到同一個節點上去處理和聚合。
- Spark中有兩種Shuffle管理類型,HashShufflManager和SortShuffleManager,Spark1.2之前是HashShuffleManager, Spark1.2引入SortShuffleManager,在Spark 2.0+版本中已經將HashShuffleManager丟棄。
- HashShuffleManager
- 普通機制
- 普通機制
- 執行流程
a)每一個map task將不同結果寫到不同的buffer中,每個buffer的大小爲 32K。buffer起到數據緩存的作用。
b)每個buffer文件最後對應一個磁盤小文件。
c)reduce task來拉取對應的磁盤小文件。
- 總結
①.map task的計算結果會根據分區器(默認是hashPartitioner)來決定寫入到哪一個磁盤小文件中去。ReduceTask會去Map端拉取相應的磁盤小文件。
②.產生的磁盤小文件的個數:
M(map task的個數)*R(reduce task的個數) - 存在的問題
產生的磁盤小文件過多,會導致以下問題:
a)在Shuffle Write過程中會產生很多寫磁盤小文件的對象。
b)在Shuffle Read過程中會產生很多讀取磁盤小文件的對象。
c)在JVM堆內存中對象過多會造成頻繁的gc,gc還無法解決運行所需要的內存 的話,就會OOM。
d)在數據傳輸過程中會有頻繁的網絡通信,頻繁的網絡通信出現通信故障的可能性大大增加,一旦網絡通信出現了故障會導致shuffle file cannot find 由於這個錯誤導致的task失敗,TaskScheduler不負責重試,由DAGScheduler負責重試Stage。
- 合併機制
合併機制示意圖
總結:
產生磁盤小文件的個數:C(core的個數)*R(reduce的個數)
- SortShuffleManager
- 普通機制
- 普通機制示意圖
- 執行流程
a)map task 的計算結果會寫入到一個內存數據結構裏面,內存數據結構默認是5M
b)在shuffle的時候會有一個定時器,不定期的去估算這個內存結構的大小,當內存結構中的數據超過5M時,比如現在內存結構中的數據爲5.01M,那麼他會申請5.01*2-5=5.02M內存給內存數據結構。
c)如果申請成功不會進行溢寫,如果申請不成功,這時候會發生溢寫磁盤。
d)在溢寫之前內存結構中的數據會進行排序分區
e)然後開始溢寫磁盤,寫磁盤是以batch的形式去寫,一個batch是1萬條數據,
f)map task執行完成後,會將這些磁盤小文件合併成一個大的磁盤文件,同時生成一個索引文件。
reduce task去map端拉取數據的時候,首先解析索引文件,根據索引文件再去拉取對應的數據。 - 總結
產生磁盤小文件的個數: 2*M(map task的個數) - bypass機制
- bypass機制示意圖
- 總結
bypass運行機制的觸發條件如下:
shuffle reduce task的數量小於spark.shuffle.sort.bypassMergeThreshold的參數值。這個值默認是200。
產生的磁盤小文件爲:2*M(map task的個數)
Shuffle文件尋址
- MapOutputTracker
MapOutputTracker是Spark架構中的一個模塊,是一個主從架構。管理磁盤小文件的地址。
MapOutputTrackerMaster是主對象,存在於Driver中。
MapOutputTrackerWorker是從對象,存在於Excutor中。 - BlockManager
BlockManager塊管理者,是Spark架構中的一個模塊,也是一個主從架構。
BlockManagerMaster,主對象,存在於Driver中。
BlockManagerMaster會在集羣中有用到廣播變量和緩存數據或者刪除緩存數據的時候,通知BlockManagerSlave傳輸或者刪除數據。
BlockManagerSlave,從對象,存在於Excutor中。
BlockManagerSlave會與BlockManagerSlave之間通信。
無論在Driver端的BlockManager還是在Excutor端的BlockManager都含有三個對象:
①DiskStore:負責磁盤的管理。
②MemoryStore:負責內存的管理。
③BlockTransferService:負責數據的傳輸。 - Shuffle文件尋址圖
- Shuffle文件尋址流程
a)當map task執行完成後,會將task的執行情況和磁盤小文件的地址封裝到MpStatus對象中,通過MapOutputTrackerWorker對象向Driver中的MapOutputTrackerMaster彙報。
b)在所有的map task執行完畢後,Driver中就掌握了所有的磁盤小文件的地址。
c)在reduce task執行之前,會通過Excutor中MapOutPutTrackerWorker向Driver端的MapOutputTrackerMaster獲取磁盤小文件的地址。
d)獲取到磁盤小文件的地址後,會通過BlockManager連接數據所在節點,然後通過BlockTransferService進行數據的傳輸。
e)BlockTransferService默認啓動5個task去節點拉取數據。默認情況下,5個task拉取數據量不能超過48M。
Spark內存管理
- 概念
Spark執行應用程序時,Spark集羣會啓動Driver和Executor兩種JVM進程,Driver負責創建SparkContext上下文,提交任務,task的分發等。Executor負責task的計算任務,並將結果返回給Driver。同時需要爲需要持久化的RDD提供儲存。Driver端的內存管理比較簡單,這裏所說的Spark內存管理針對Executor端的內存管理。
Spark內存管理分爲靜態內存管理和統一內存管理,Spark1.6之前使用的是靜態內存管理,Spark1.6之後引入了統一內存管理。
靜態內存管理中存儲內存、執行內存和其他內存的大小在 Spark 應用程序運行期間均爲固定的,但用戶可以應用程序啓動前進行配置。
統一內存管理與靜態內存管理的區別在於儲存內存和執行內存共享同一塊空間,可以互相借用對方的空間。
Spark1.6以上版本默認使用的是統一內存管理,可以通過參數spark.memory.useLegacyMode 設置爲true(默認爲false)使用靜態內存管理。
靜態內存管理分佈圖
統一內存管理分佈圖
- reduce 中OOM如何處理?
1)減少每次拉取的數據量
2)提高shuffle聚合的內存比例
3)提高Excutor的總內存
Shuffle調優
- SparkShuffle調優配置項如何使用?
1)在代碼中,不推薦使用,硬編碼。
new SparkConf().set(“spark.shuffle.file.buffer”,”64”)
2)在提交spark任務的時候,推薦使用。
spark-submit --conf spark.shuffle.file.buffer=64 –conf ….
3)在conf下的spark-default.conf配置文件中,不推薦,因爲是寫死後所有應 用程序都要用。