[看圖說話] 基於Spark UI性能優化與調試——初級篇java
Spark有幾種部署的模式,單機版、集羣版等等,平時單機版在數據量不大的時候能夠跟傳統的java程序同樣進行斷電調試、可是在集羣上調試就比較麻煩了...遠程斷點不太方便,只能經過Log的形式進行數據分析,利用spark ui作性能調整和優化。
那麼本篇就介紹下如何利用Ui作性能分析,由於本人的經驗也不是很豐富,因此只能做爲一個入門的介紹。api
大致上會按照下面的思路進行講解:緩存
怎麼訪問Spark UI
SparkUI能看到什麼東西?job,stage,storage,environment,excutors
調優的一些經驗總結
Spark UI入口性能優化
若是是單機版本,在單機調試的時候輸出信息中已經提示了UI的入口:服務器
17/02/26 13:55:48 INFO SparkEnv: Registering OutputCommitCoordinator
17/02/26 13:55:49 INFO Utils: Successfully started service 'SparkUI' on port 4040.
17/02/26 13:55:49 INFO SparkUI: Started SparkUI at http://192.168.1.104:4040
17/02/26 13:55:49 INFO Executor: Starting executor ID driver on host localhost
單機調試的時候,能夠直接登錄:http://192.168.1.104:4040多線程
若是是集羣模式,能夠經過Spark日誌服務器xxxxx:18088者yarn的UI進入到應用xxxx:8088,進入相應的Spark UI界面。分佈式
主頁介紹工具
上面就是Spark的UI主頁,首先進來能看到的是Spark當前應用的job頁面,在上面的導航欄:性能
1 表明job頁面,在裏面能夠看到當前應用分析出來的全部任務,以及全部的excutors中action的執行時間。
2 表明stage頁面,在裏面能夠看到應用的全部stage,stage是按照寬依賴來區分的,所以粒度上要比job更細一些
3 表明storage頁面,咱們所作的cache persist等操做,都會在這裏看到,能夠看出來應用目前使用了多少緩存
4 表明environment頁面,裏面展現了當前spark所依賴的環境,好比jdk,lib等等
5 表明executors頁面,這裏能夠看到執行者申請使用的內存以及shuffle中input和output等數據
6 這是應用的名字,代碼中若是使用setAppName,就會顯示在這裏
7 是job的主頁面。
模塊講解測試
下面挨個介紹一下各個頁面的使用方法和實踐,爲了方便分析,我這裏直接使用了分佈式計算裏面最經典的helloworld程序——WordCount,這個程序用於統計某一段文本中一個單詞出現的次數。原始的文本以下:
for the shadow of lost knowledge at least protects you from many illusions
上面這句話是有一次逛知乎,一個標題爲 讀那麼多書,最後也沒記住多少,還爲何讀書?其中有一個回覆,引用了上面的話,也是我最喜歡的一句。意思是:「知識,哪怕是知識的幻影,也會成爲你的鎧甲,保護你不被愚昧反噬」(來自知乎——《爲何讀書?》)
程序代碼以下:
public static void main(String[] args) throws InterruptedException {
SparkConf sparkConf = new SparkConf(); sparkConf.setMaster("local[2]"); sparkConf.setAppName("test-for-spark-ui"); JavaSparkContext sc = new JavaSparkContext(sparkConf); //知識,哪怕是知識的幻影,也會成爲你的鎧甲,保護你不被愚昧反噬。 JavaPairRDD<String,Integer> counts = sc.textFile( "C:\\Users\\xinghailong\\Desktop\\你爲何要讀書.txt" ) .flatMap(line -> Arrays.asList(line.split(" "))) .mapToPair(s -> new Tuple2<String,Integer>(s,1)) .reduceByKey((x,y) -> x+y); counts.cache(); List<Tuple2<String,Integer>> result = counts.collect(); for(Tuple2<String,Integer> t2 : result){ System.out.println(t2._1+" : "+t2._2); } sc.stop();
}
這個程序首先建立了SparkContext,而後讀取文件,先使用
進行切分,再把每一個單詞轉換成二元組,再根據key進行累加,最後輸出打印。爲了測試storage的使用,我這對計算的結果添加了緩存。
job頁面
主頁能夠分爲兩部分,一部分是event timeline,另外一部分是進行中和完成的job任務。
第一部分event timeline展開後,能夠看到executor建立的時間點,以及某個action觸發的算子任務,執行的時間。經過這個時間圖,能夠快速的發現應用的執行瓶頸,觸發了多少個action。
第二部分的圖表,顯示了觸發action的job名字,它一般是某個count,collect等操做。有spark基礎的人都應該知道,在spark中rdd的計算分爲兩類,一類是transform轉換操做,一類是action操做,只有action操做纔會觸發真正的rdd計算。具體的有哪些action能夠觸發計算,能夠參考api。collect at test2.java:27描述了action的名字和所在的行號,這裏的行號是精準匹配到代碼的,因此經過它能夠直接定位到任務所屬的代碼,這在調試分析的時候是很是有幫助的。Duration顯示了該action的耗時,經過它也能夠對代碼進行專門的優化。最後的進度條,顯示了該任務失敗和成功的次數,若是有失敗的就須要引發注意,由於這種狀況在生產環境可能會更廣泛更嚴重。點擊能進入該action具體的分析頁面,能夠看到DAG圖等詳細信息。
stage頁面
在Spark中job是根據action操做來區分的,另外任務還有一個級別是stage,它是根據寬窄依賴來區分的。
窄依賴是指前一個rdd計算能出一個惟一的rdd,好比map或者filter等;寬依賴則是指多個rdd生成一個或者多個rdd的操做,好比groupbykey reducebykey等,這種寬依賴一般會進行shuffle。
所以Spark會根據寬窄依賴區分stage,某個stage做爲專門的計算,計算完成後,會等待其餘的executor,而後再統一進行計算。
stage頁面的使用基本上跟job相似,不過多了一個DAG圖。這個DAG圖也叫做血統圖,標記了每一個rdd從建立到應用的一個流程圖,也是咱們進行分析和調優很重要的內容。好比上面的wordcount程序,就會觸發acton,而後生成一段DAG圖:
從這個圖能夠看出,wordcount會生成兩個dag,一個是從讀數據到切分到生成二元組,第二個進行了reducebykey,產生shuffle。
點擊進去還能夠看到詳細的DAG圖,鼠標移到上面,能夠看到一些簡要的信息。
storage頁面
storage頁面能看出目前使用的緩存,點擊進去能夠看到具體在每一個機器上,使用的block的狀況。
environment頁面
這個頁面通常不太用,由於環境基本上不會有太多差別的,不用時刻關注它。
excutors頁面
這個頁面比較經常使用了,一方面經過它能夠看出來每一個excutor是否發生了數據傾斜,另外一方面能夠具體分析目前的應用是否產生了大量的shuffle,是否能夠經過數據的本地性或者減少數據的傳輸來減小shuffle的數據量。
調優的經驗總結
1 輸出信息
在Spark應用裏面能夠直接使用System.out.println把信息輸出出來,系統會直接攔截out輸出到spark的日誌。像咱們使用的yarn做爲資源管理系統,在yarn的日誌中就能夠直接看到這些輸出信息了。這在數據量很大的時候,作一些show()(默認顯示20),count() 或者 take(10)的時候會很方便。
2 內存不夠
當任務失敗,收到sparkContext shutdown的信息時,基本都是執行者的內存不夠。這個時候,一方面能夠調大--excutor-memory參數,另外一方面仍是得回去看看程序。若是受限於系統的硬件條件,沒法加大內存,能夠採用局部調試法,檢查是在哪裏出現的內存問題。好比,你的程序分紅幾個步驟,一步一步的打包運行,最後檢查出現問題的點就能夠了。
3 ThreadPool
線程池不夠,這個是由於--excutor-core給的太少了,出現線程池不夠用的狀況。這個時候就須要調整參數的配置了。
4 physical memory不夠
這種問題通常是driver memory不夠致使的,driver memory一般存儲了以一些調度方面的信息,這種狀況頗有多是你的調度過於複雜,或者是內部死循環致使。
5 合理利用緩存
在Spark的計算中,不太建議直接使用cache,萬一cache的量很大,可能致使內存溢出。能夠採用persist的方式,指定緩存的級別爲MEMORY_AND_DISK,這樣在內存不夠的時候,能夠把數據緩存到磁盤上。另外,要合理的設計代碼,恰當地使用廣播和緩存,廣播的數據量太大會對傳輸帶來壓力,緩存過多未及時釋放,也會致使內存佔用。通常來講,你的代碼在須要重複使用某一個rdd的時候,才須要考慮進行緩存,而且在不使用的時候,要及時unpersist釋放。
6 儘可能避免shuffle
這個點,在優化的過程當中是很重要的。好比你須要把兩個rdd按照某個key進行groupby,而後在進行leftouterjoin,這個時候必定要考慮大小表的問題。若是把大表關聯到小表,那麼性能極可能會很慘。而只須要簡單的調換一下位置,性能就可能提高好幾倍。
寫在最後
大數據計算老是充滿了各類神奇的色彩,節點之間的分佈式,單節點內多線程的並行化,只有多去了解一些原理性的東西,才能用好這些工具。
最後仍是獻上最喜歡的那句話——知識,哪怕是知識的幻影,也會成爲你的鎧甲,保護你不被愚昧反噬。