一般而言常見的Spark的性能瓶頸有哪些?
謝邀這個問題是比較複雜的,不能一概而論。
shuffle是spark及其他分散式計算框架最核心的問題之一,為了提高shuffle的效率,spark也做了很多迭代更新,最新的spark1.2.0中新特性之一就是shuffle機制變成了sorted-bashed shuffle
In 1.2 Spark core upgrades two major subsystems to improve the performance and stability of very large scale shuffles. The first is Spark』s communication manager used during bulk transfers, which upgrades to a netty-based implementation. The second is Spark』s shuffle mechanism, which upgrades to the 「sort based」 shuffle initially released in Spark 1.1. These both improve the performance and stability of very large scale shuffles. Spark also adds an elastic scaling mechanism designed to improve cluster utilization during long running ETL-style jobs. This is currently supported on YARN and will make its way to other cluster managers in future versions. Finally, Spark 1.2 adds support for Scala 2.11. For instructions on building for Scala 2.11 see the build documentation.
所以,關於shuffle,可以這麼說,shuffle是影響spark作業執行效率的關鍵因素,但是一概而論的說shuffle是最耗時的步驟還是有失偏頗。實際上,由於spark shuffle的日臻完善,spark 作業計算的時間瓶頸不只是shuffle引起的。
針對不同的作業類型,在對集群進行良好配置的提前下,spark作業最耗時的部分往往是因為集群資源的限制,主要體現在三個方面:CPU、DISK IO、NETWORK IO。spark將作業拆分成的單元是stage,不同的stage內部執行不同的邏輯。stage內部既有io操作,也有cpu計算操作,還會有network(主要是shuffle引起的),當這三個部分其中某一部分所佔的比例較大時,在資源佔用上就會體現出這一部分的bottleneck。例如,
val data = sc.textFile("...").count()
這樣一個stage,就是簡單的從hdfs中讀取文本數據計算有多少行。幾乎沒有cpu操作,shuffle的量也很小,主要的操作在DISK IO上,因此,這個stage耗時最長的部分就是在磁碟讀寫上。
因此,判斷一個作業最耗時的部分,需要實際的去分析stage的執行邏輯,結合實際的資源佔用情況,這樣才能的到準確完整的答案影響性能的主要因素是Shuffle,可以優化代碼減少不必要的Stage數量及Shuffle數據量以改進Shuffle性能。由於Shuffle的中間結果數據都是要落磁碟的,所以可以考慮給伺服器加SSD(一般100G左右就夠了),將Spark的tmp目錄設置為SSD目錄,性能提升20%左右。
其次是IO,網路IO建議上萬兆網路(這也是影響Shuffle性能的一個因素),對於磁碟IO,一般是給伺服器設置多塊單盤,不要做RAID!掛載磁碟時設置noatime,提高磁碟IO性能。
最後是CPU,Spark做計算如果數據已經載入到內存了,CPU就比較容易成為計算瓶頸,代碼方面主要是優化計算,減少計算量,同時也要關注計算任務是否有數據傾斜的現象;硬體方面只能換性能更強勁的CPU了。
如果你不確定自己的Spark應用性能問題出在哪,就去分析Spark Web UI,裡面有詳細的各項metrics數據。具體任務具體分析吧,有些任務是單map的就沒有shuffle。可以通過各種計數器來觀察整個程序的運行情況。
看到多數人都說了硬體上的瓶頸,例如磁碟,網路或者CPU。我們研究發現,Spark框架與實現語言上同樣有瓶頸,也就是說除了眾所周知的shuffle,cache的數據也會造成瓶頸哦!詳細請參考「Lifetime-Based Memory Management for Distributed Data Processing System」(VLDB2016)和「Yak: A High-performance Big-data-friendly Garbage Collector」(OSDI2016)。
Spark是採用Java和Scala語言編寫,運行在JVM上。因此,所有處理的數據都是以對象Object的形式存在的。對JVM來說,Object都具有兩個特點:
(1)大小。內存膨脹的問題是大數據處理中一個典型的問題,參考「A Bloat-aware Design for Big Data Application」(ISMM2013)。對象形式會引入許多無關的引用、鎖結構、描述符等,導致其內存中的大小相比於對象本身所攜帶的Value要大得多。例如,一個int值只佔4個位元組,但是裝箱成一個Integer對象,遠遠不止4個位元組了。(2)生命周期。JVM有自己的垃圾回收機制,根據對象的生命周期來決定是否需要做垃圾回收。任何對象都有自己的生命周期。由於Spark本身支持cache數據到內存,所以JVM中會有cache的Object。再看shuffle,shufflewrite和shuffle
read階段需要用buffer保存所有處理的中間結果(ExternalSorter),然後再寫入磁碟,因此Shuffle
buffer中也包含了非常多的Object。無論是Cache的Object還是Shuffle Buffer中的Object,它們的生命周期都比較長。當對象數量增加時,有限的內存空間就會因為這些長生命周期的大對象顯得非常有壓力,最直接的影響就是頻繁的觸發JVM的垃圾回收機制,Full GC本身就會導致大量開銷,頻繁的觸發Full GC會導致Spark性能急劇下降。
這是所有自動內存管理機制都會面臨的一個問題,提高了開發效率卻面臨著大數據處理時的低效內存管理。
我們解決這個問題的關鍵就是:Lifetime-Based Memory Management。基於對象生命周期的區域化內存管理可以有效的解決Spark在內存管理上的瓶頸,基本上消除了GC的影響。詳細可以參考論文,歡迎交流。對於不同的計算場景,io,shuffle,cpu都有可能成為計算瓶頸。一般來說,做統計的時候io是最大的瓶頸,做數據挖掘的時候比較慢的是shuffle和cpu。
不僅僅是spark,shuffle應該是大家都面臨的問題。
推薦閱讀:
※Scala快速入門系列:聲明變數、控制結構與函數、常用數組操作
※Spark 2017歐洲技術峰會摘要(Spark 生態體系分類)
※如何利用spark快速計算笛卡爾積?
※Scala 究竟好在那裡?