spark-JVM調優原理

性能調優java

  1. 常規性能調優:分配資源、並行度。。。等
  2. JVM調優(Java虛擬機):JVM相關的參數,一般狀況下,若是你的硬件配置、基礎的JVM的配置,均可以的話,JVM一般不會形成太嚴重的性能問題;反而更多的是,在troubleshooting中,JVM佔了很重要的地位;JVM形成線上的spark做業的運行報錯,甚至失敗(好比OOM)。
  3. shuffle調優(至關重要):spark在執行groupByKey、reduceByKey等操做時的,shuffle環節的調優。這個很重要。shuffle調優,其實對spark做業的性能的影響,是至關之高!!!經驗:在spark做業的運行過程當中,只要一牽扯到有shuffle的操做,基本上shuffle操做的性能消耗,要佔到整個spark做業的50%~90%。10%用來運行map等操做,90%耗費在shuffle操做。
  4. spark操做調優(spark算子調優,比較重要):有些算子的性能,是比其餘一些算子的性能要高的。foreachPartition替代foreach。

若是一旦遇到合適的狀況,效果仍是不錯的。算法

一、分配資源、並行度、RDD架構與緩存
二、shuffle調優
三、spark算子調優
四、JVM調優、廣播大變量。。。數據庫

JVM調優原理概述。api

JVM調優裏面全部官方都推薦來下降cache操做佔比緩存

  • 理論基礎:spark是用scala開發的。你們不要覺得scala就跟java一點關係都沒有了,這是一個很常見的錯誤。spark的scala代碼調用了不少java api。scala也是運行在java虛擬機中的。spark是運行在java虛擬機中的。java虛擬機可能會產生什麼樣的問題:內存不足??!!咱們的RDD的緩存、task運行定義的算子函數,可能會建立不少對象。均可能會佔用大量內存,沒搞好的話,可能致使JVM出問題。
  • 堆內存:
    • 存放咱們建立的一些對象,堆內存分爲年輕帶young generation和老年帶old generation,年輕帶內部又分爲三塊,Eden區域比較大,兩個survivor區域比較小存活區域咱們在spark task執行算子函數(咱們本身寫的針對RDD的操做),可能會建立不少對象,這些對象,都是要放入JVM年輕代中的。每一次放對象的時候,都是放入eden區域,和其中一個survivor區域;另一個survivor區域是空閒的。當eden區域和一個survivor區域放滿了之後(spark運行過程當中,產生的對象實在太多了),就會觸發minor gc,小型垃圾回收。垃圾回收器gc會把再也不使用的對象,從內存中清空,給後面新建立的對象騰出來點兒地方。
    • 清理掉了再也不使用的對象以後,那麼也會將存活下來的對象(還要繼續使用的),放入以前空閒的那一個survivor區域中。這裏可能會出現一個問題。默認eden、survior1和survivor2的內存佔比是8:1:1。問題是,若是存活下來的對象是1.5,一個survivor區域放不下。此時就可能經過JVM的擔保機制(不一樣JVM版本可能對應的行爲),將多餘的對象,直接放入老年代了。
    • 若是你的JVM內存不夠大的話,可能致使頻繁的年輕代內存滿溢,頻繁的進行minor gc。頻繁的minor gc會致使短期內,有些存活的對象,屢次垃圾回收都沒有回收掉。就是那些一直在用的又不能被釋放的就頻繁的倒來倒去!會致使這種短聲明週期(其實不必定是要長期使用的)對象,每回收一次,年齡長一歲!年齡過大,垃圾回收次數太多尚未回收到,跑到老年代。
    • 說白了就是短聲明週期對象卻跑到老年代裏面去了!!!原本是短週期的,結果倒來倒去跑到老年代裏面去了,理想狀況下,老年代都是放一些生命週期很長的對象,數量應該是不多的。好比數據庫鏈接池,數據庫鏈接池原本就不多。
    • 簡而言之,老年代中,可能會由於內存不足,囤積一大堆,短生命週期的,原本應該在年輕代中的,可能立刻就要被回收掉的對象。此時,可能致使老年代頻繁滿溢。頻繁進行full gc(全局/全面垃圾回收)。full gc就會去回收老年代中的對象。full gc因爲這個算法的設計,是針對的是,老年代中的對象數量不多,滿溢進行full gc的頻率應該不多,所以採起了不太複雜,可是耗費性能和時間的垃圾回收算法。full gc很慢。
    • full gc / minor gc,不管是快,仍是慢,都會致使jvm的工做線程中止工做,stop the world。簡而言之,就是說,gc的時候,spark中止工做了。等着垃圾回收結束。
  • 內存不充足的時候,問題:
    • 頻繁minor gc,也會致使頻繁spark中止工做;
    • 老年代囤積大量活躍對象(短生命週期的對象),致使頻繁full gc,full gc時間很長,短則數十秒,長則數分鐘,甚至數小時。可能致使spark長時間中止工做;
    • 嚴重影響我們的spark的性能和運行的速度。

如何解決?架構

  • JVM調優的第一個點:下降cache操做的內存佔比
    • spark中,堆內存又被劃分紅了兩塊兒,一起是專門用來給RDD的cache、persist操做進行RDD數據緩存用的;另一塊兒,就是咱們剛纔所說的,用來給spark算子函數的運行使用的,存放函數中本身建立的對象。
    • 默認狀況下,給RDD cache操做的內存佔比,是0.6,60%的內存都給了cache操做了。可是問題是,若是某些狀況下,cache不是那麼的緊張,問題在於task算子函數中建立的對象過多,而後內存又不太大,致使了頻繁的minor gc,甚至頻繁full gc,致使spark頻繁的中止工做。性能影響會很大。
    • 針對上述這種狀況,你們能夠在spark uich查看。yarn去運行的話,那麼就經過yarn的界面,去查看你的spark做業的運行統計,很簡單,你們一層一層點擊進去就好。能夠看到每一個stage的運行狀況,包括每一個task的運行時間、gc時間等等。若是發現gc太頻繁,時間太長。此時就能夠適當調節這個比例。下降cache操做的內存佔比,大不了用persist操做,選擇將一部分緩存的RDD數據寫入磁盤,或者序列化方式,配合Kryo序列化類,減小RDD緩存的內存佔用;下降cache操做內存佔比;對應的,算子函數的內存佔比就提高了。這個時候,可能,就能夠減小minor gc的頻率,同時減小full gc的頻率。對性能的提高是有必定的幫助的。一句話,讓task執行算子函數時,有更多的內存可使用。
      spark.storage.memoryFraction,0.6 -> 0.5 -> 0.4 -> 0.2

       

        你們能夠本身去調,而後觀察spark做業的運行統計!!!而後看看總體運行時間有沒有改善!gc是否頻繁,gc時間等!上述比例均可以調!根據不一樣需求來作!jvm

.set("spark.storage.memoryFraction", "0.5")  
相關文章
相關標籤/搜索