Spark有幾種部署的模式,單機(jī)版、集群版等等,平時(shí)單機(jī)版在數(shù)據(jù)量不大的時(shí)候可以跟傳統(tǒng)的java程序一樣進(jìn)行斷電調(diào)試、但是在集群上調(diào)試就比較麻煩了...遠(yuǎn)程斷點(diǎn)不太方便,只能通過Log的形式進(jìn)行數(shù)據(jù)分析,利用spark ui做性能調(diào)整和優(yōu)化。

那么本篇就介紹下如何利用Ui做性能分析,因?yàn)楸救说慕?jīng)驗(yàn)也不是很豐富,所以只能作為一個(gè)入門的介紹。

大體上會(huì)按照下面的思路進(jìn)行講解:

  • 怎么訪問Spark UI

  • SparkUI能看到什么東西?job,stage,storage,environment,excutors

  • 調(diào)優(yōu)的一些經(jīng)驗(yàn)總結(jié)

Spark UI入口

如果是單機(jī)版本,在單機(jī)調(diào)試的時(shí)候輸出信息中已經(jīng)提示了UI的入口:

17/02/26 13:55:48 INFO SparkEnv: Registering OutputCommitCoordinator17/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:404017/02/26 13:55:49 INFO Executor: Starting executor ID driver on host localhost

單機(jī)調(diào)試的時(shí)候,可以直接登陸:http://192.168.1.104:4040

如果是集群模式,可以通過Spark日志服務(wù)器xxxxx:18088者yarn的UI進(jìn)入到應(yīng)用xxxx:8088,進(jìn)入相應(yīng)的Spark UI界面。

主頁介紹

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

上面就是Spark的UI主頁,首先進(jìn)來能看到的是Spark當(dāng)前應(yīng)用的job頁面,在上面的導(dǎo)航欄:

  • 1 代表job頁面,在里面可以看到當(dāng)前應(yīng)用分析出來的所有任務(wù),以及所有的excutors中action的執(zhí)行時(shí)間。

  • 2 代表stage頁面,在里面可以看到應(yīng)用的所有stage,stage是按照寬依賴來區(qū)分的,因此粒度上要比job更細(xì)一些

  • 3 代表storage頁面,我們所做的cache persist等操作,都會(huì)在這里看到,可以看出來應(yīng)用目前使用了多少緩存

  • 4 代表environment頁面,里面展示了當(dāng)前spark所依賴的環(huán)境,比如jdk,lib等等

  • 5 代表executors頁面,這里可以看到執(zhí)行者申請(qǐng)使用的內(nèi)存以及shuffle中input和output等數(shù)據(jù)

  • 6 這是應(yīng)用的名字,代碼中如果使用setAppName,就會(huì)顯示在這里

  • 7 是job的主頁面。

模塊講解

下面挨個(gè)介紹一下各個(gè)頁面的使用方法和實(shí)踐,為了方便分析,我這里直接使用了分布式計(jì)算里面最經(jīng)典的helloworld程序——WordCount,這個(gè)程序用于統(tǒng)計(jì)某一段文本中一個(gè)單詞出現(xiàn)的次數(shù)。原始的文本如下:

for the shadow of lost knowledge at least protects you from many illusions

上面這句話是有一次逛知乎,一個(gè)標(biāo)題為 讀那么多書,最后也沒記住多少,還為什么讀書?其中有一個(gè)回復(fù),引用了上面的話,也是我最喜歡的一句。意思是:“知識(shí),哪怕是知識(shí)的幻影,也會(huì)成為你的鎧甲,保護(hù)你不被愚昧反噬”(來自知乎——《為什么讀書?》)

程序代碼如下:

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);        //知識(shí),哪怕是知識(shí)的幻影,也會(huì)成為你的鎧甲,保護(hù)你不被愚昧反噬。
        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();
}

這個(gè)程序首先創(chuàng)建了SparkContext,然后讀取文件,先使用` `進(jìn)行切分,再把每個(gè)單詞轉(zhuǎn)換成二元組,再根據(jù)key進(jìn)行累加,最后輸出打印。為了測(cè)試storage的使用,我這對(duì)計(jì)算的結(jié)果添加了緩存。

job頁面

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

主頁可以分為兩部分,一部分是event timeline,另一部分是進(jìn)行中和完成的job任務(wù)。

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

第一部分event timeline展開后,可以看到executor創(chuàng)建的時(shí)間點(diǎn),以及某個(gè)action觸發(fā)的算子任務(wù),執(zhí)行的時(shí)間。通過這個(gè)時(shí)間圖,可以快速的發(fā)現(xiàn)應(yīng)用的執(zhí)行瓶頸,觸發(fā)了多少個(gè)action。

第二部分的圖表,顯示了觸發(fā)action的job名字,它通常是某個(gè)count,collect等操作。有spark基礎(chǔ)的人都應(yīng)該知道,在spark中rdd的計(jì)算分為兩類,一類是transform轉(zhuǎn)換操作,一類是action操作,只有action操作才會(huì)觸發(fā)真正的rdd計(jì)算。具體的有哪些action可以觸發(fā)計(jì)算,可以參考api。collect at test2.java:27描述了action的名字和所在的行號(hào),這里的行號(hào)是精準(zhǔn)匹配到代碼的,所以通過它可以直接定位到任務(wù)所屬的代碼,這在調(diào)試分析的時(shí)候是非常有幫助的。Duration顯示了該action的耗時(shí),通過它也可以對(duì)代碼進(jìn)行專門的優(yōu)化。最后的進(jìn)度條,顯示了該任務(wù)失敗和成功的次數(shù),如果有失敗的就需要引起注意,因?yàn)檫@種情況在生產(chǎn)環(huán)境可能會(huì)更普遍更嚴(yán)重。點(diǎn)擊能進(jìn)入該action具體的分析頁面,可以看到DAG圖等詳細(xì)信息。

stage頁面

在Spark中job是根據(jù)action操作來區(qū)分的,另外任務(wù)還有一個(gè)級(jí)別是stage,它是根據(jù)寬窄依賴來區(qū)分的。
大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

窄依賴是指前一個(gè)rdd計(jì)算能出一個(gè)唯一的rdd,比如map或者filter等;寬依賴則是指多個(gè)rdd生成一個(gè)或者多個(gè)rdd的操作,比如groupbykey reducebykey等,這種寬依賴通常會(huì)進(jìn)行shuffle。

因此Spark會(huì)根據(jù)寬窄依賴區(qū)分stage,某個(gè)stage作為專門的計(jì)算,計(jì)算完成后,會(huì)等待其他的executor,然后再統(tǒng)一進(jìn)行計(jì)算。

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

stage頁面的使用基本上跟job類似,不過多了一個(gè)DAG圖。這個(gè)DAG圖也叫作血統(tǒng)圖,標(biāo)記了每個(gè)rdd從創(chuàng)建到應(yīng)用的一個(gè)流程圖,也是我們進(jìn)行分析和調(diào)優(yōu)很重要的內(nèi)容。比如上面的wordcount程序,就會(huì)觸發(fā)acton,然后生成一段DAG圖:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

從這個(gè)圖可以看出,wordcount會(huì)生成兩個(gè)dag,一個(gè)是從讀數(shù)據(jù)到切分到生成二元組,第二個(gè)進(jìn)行了reducebykey,產(chǎn)生shuffle。

點(diǎn)擊進(jìn)去還可以看到詳細(xì)的DAG圖,鼠標(biāo)移到上面,可以看到一些簡(jiǎn)要的信息。
大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

storage頁面

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

storage頁面能看出目前使用的緩存,點(diǎn)擊進(jìn)去可以看到具體在每個(gè)機(jī)器上,使用的block的情況。

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

environment頁面

這個(gè)頁面一般不太用,因?yàn)榄h(huán)境基本上不會(huì)有太多差異的,不用時(shí)刻關(guān)注它。

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

excutors頁面

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

這個(gè)頁面比較常用了,一方面通過它可以看出來每個(gè)excutor是否發(fā)生了數(shù)據(jù)傾斜,另一方面可以具體分析目前的應(yīng)用是否產(chǎn)生了大量的shuffle,是否可以通過數(shù)據(jù)的本地性或者減小數(shù)據(jù)的傳輸來減少shuffle的數(shù)據(jù)量。

調(diào)優(yōu)的經(jīng)驗(yàn)總結(jié)

1 輸出信息

在Spark應(yīng)用里面可以直接使用System.out.println把信息輸出出來,系統(tǒng)會(huì)直接攔截out輸出到spark的日志。像我們使用的yarn作為資源管理系統(tǒng),在yarn的日志中就可以直接看到這些輸出信息了。這在數(shù)據(jù)量很大的時(shí)候,做一些show()(默認(rèn)顯示20),count() 或者 take(10)的時(shí)候會(huì)很方便。

2 內(nèi)存不夠

當(dāng)任務(wù)失敗,收到sparkContext shutdown的信息時(shí),基本都是執(zhí)行者的內(nèi)存不夠。這個(gè)時(shí)候,一方面可以調(diào)大--excutor-memory參數(shù),另一方面還是得回去看看程序。如果受限于系統(tǒng)的硬件條件,無法加大內(nèi)存,可以采用局部調(diào)試法,檢查是在哪里出現(xiàn)的內(nèi)存問題。比如,你的程序分成幾個(gè)步驟,一步一步的打包運(yùn)行,最后檢查出現(xiàn)問題的點(diǎn)就可以了。

3 ThreadPool

線程池不夠,這個(gè)是因?yàn)?-excutor-core給的太少了,出現(xiàn)線程池不夠用的情況。這個(gè)時(shí)候就需要調(diào)整參數(shù)的配置了。

4 physical memory不夠

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

這種問題一般是driver memory不夠?qū)е碌模琩river memory通常存儲(chǔ)了以一些調(diào)度方面的信息,這種情況很有可能是你的調(diào)度過于復(fù)雜,或者是內(nèi)部死循環(huán)導(dǎo)致。

5 合理利用緩存

在Spark的計(jì)算中,不太建議直接使用cache,萬一cache的量很大,可能導(dǎo)致內(nèi)存溢出??梢圆捎胮ersist的方式,指定緩存的級(jí)別為MEMORY_AND_DISK,這樣在內(nèi)存不夠的時(shí)候,可以把數(shù)據(jù)緩存到磁盤上。另外,要合理的設(shè)計(jì)代碼,恰當(dāng)?shù)厥褂脧V播和緩存,廣播的數(shù)據(jù)量太大會(huì)對(duì)傳輸帶來壓力,緩存過多未及時(shí)釋放,也會(huì)導(dǎo)致內(nèi)存占用。一般來說,你的代碼在需要重復(fù)使用某一個(gè)rdd的時(shí)候,才需要考慮進(jìn)行緩存,并且在不使用的時(shí)候,要及時(shí)unpersist釋放。

6 盡量避免shuffle

這個(gè)點(diǎn),在優(yōu)化的過程中是很重要的。比如你需要把兩個(gè)rdd按照某個(gè)key進(jìn)行g(shù)roupby,然后在進(jìn)行l(wèi)eftouterjoin,這個(gè)時(shí)候一定要考慮大小表的問題。如果把大表關(guān)聯(lián)到小表,那么性能很可能會(huì)很慘。而只需要簡(jiǎn)單的調(diào)換一下位置,性能就可能提升好幾倍。

寫在最后

大數(shù)據(jù)計(jì)算總是充滿了各種神奇的色彩,節(jié)點(diǎn)之間的分布式,單節(jié)點(diǎn)內(nèi)多線程的并行化,只有多去了解一些原理性的東西,才能用好這些工具。

最后還是獻(xiàn)上最喜歡的那句話——知識(shí),哪怕是知識(shí)的幻影,也會(huì)成為你的鎧甲,保護(hù)你不被愚昧反噬。

參考

  • Understanding your Apache Spark Application Through Visualization

  • 《Spark大數(shù)據(jù)處理》—— 這本書我看第一遍的時(shí)候給了個(gè)差評(píng),但是經(jīng)過一段時(shí)間的應(yīng)用實(shí)踐后,我發(fā)現(xiàn),這本書要比《Spark快速大數(shù)據(jù)分析》搞一個(gè)檔次,所以在這里再次推薦一下。