Spark trouble shooting

yarn-client模式下導致的網卡流量激增

yarn-client模式下,會產生什麼樣的問題呢?

由於咱們的driver是啓動在本地機器的,而且driver是全權負責所有的任務的調度的,
也就是說要跟yarn集羣上運行的多個executor進行頻繁的通信(中間有task的啓動消息、
task的執行統計消息、task的運行狀態、shuffle的輸出結果)。

咱們來想象一下。比如你的executor有100個,stage有10個,task有1000個。每個stage運行的時候,
都有1000個task提交到executor上面去運行,平均每個executor有10個task。接下來問題來了,
driver要頻繁地跟executor上運行的1000個task進行通信。通信消息特別多,通信的頻率特別高。
運行完一個stage,接着運行下一個stage,又是頻繁的通信。

在整個spark運行的生命週期內,都會頻繁的去進行通信和調度。所有這一切通信和調度都是從你的
本地機器上發出去的,和接收到的。這是最要人命的地方。你的本地機器,很可能在30分鐘內
(spark作業運行的週期內),進行頻繁大量的網絡通信。那麼此時,你的本地機器的網絡通信負載
是非常非常高的。會導致你的本地機器的網卡流量會激增!!!

你的本地機器的網卡流量激增,當然不是一件好事了。因爲在一些大的公司裏面,對每臺機器的使用情況,
都是有監控的。不會允許單個機器出現耗費大量網絡帶寬等等這種資源的情況。運維人員不會允許。
可能對公司的網絡,或者其他(你的機器還是一臺虛擬機),如果你是一臺虛擬機的話,和其他機器
共享網卡的話,可能對其他機器,公司整個網絡環境,都會有負面和惡劣的影響。

解決的方法:

實際上解決的方法很簡單,就是心裏要清楚,yarn-client模式是什麼情況下,可以使用的?
yarn-client模式,通常咱們就只會使用在測試環境中,你寫好了某個spark作業,打了一個jar包,
在某臺測試機器上,用yarn-client模式去提交一下。因爲測試的行爲是偶爾爲之的,
不會長時間連續提交大量的spark作業去測試。還有一點好處,yarn-client模式提交,
可以在本地機器觀察到詳細全面的log。通過查看log,可以去解決線上報錯的故障(troubleshooting)、
對性能進行觀察並進行性能調優。

實際上線了以後,在生產環境中,都得用yarn-cluster模式,去提交你的spark作業。
yarn-cluster模式,就跟你的本地機器引起的網卡流量激增的問題,就沒有關係了。也就是說,
就算有問題,也應該是yarn運維團隊和基礎運維團隊之間的事情了。
他們去考慮Yarn集羣裏面每臺機器是虛擬機還是物理機呢?網卡流量激增後會不會對其他東西產生影響呢?
如果網絡流量激增,要不要給Yarn集羣增加一些網絡帶寬等等這些東西。那就是他們倆個團隊的事情了,
和你就沒有關係了

使用了yarn-cluster模式以後,
就不是你的本地機器運行Driver,進行task調度了。是yarn集羣中,某個節點會運行driver進程,
負責task調度。

解決算子函數返回NULL導致問題

在算子函數中,返回null

return actionRDD.mapToPair(new PairFunction<Row, String, Row>() {

    private static final long serialVersionUID = 1L;

    @Override
    public Tuple2<String, Row> call(Row row) throws Exception {
        return new Tuple2<String, Row>("-666", RowFactory.createRow("-999"));  
         return null
    }       
});

大家可以看到,在有些算子函數裏面,是需要我們有一個返回值的。但是,有時候,我們可能對某些值,
就是不想有什麼返回值。
我們如果直接返回NULL的話,會報錯的!!!

如果碰到你的確是對於某些值,不想要有返回值的話,有一個解決的辦法:

1、在返回的時候,返回一些特殊的值,不要返回null,比如“-999”
2、在通過算子獲取到了一個RDD之後,可以對這個RDD執行filter操作,進行數據過濾。
filter內,可以對數據進行判定,如果是-999,那麼就返回false,給過濾掉就可以了。
3、大家不要忘了,之前咱們講過的那個算子調優裏面的coalesce算子,在filter之後,
可以使用coalesce算子壓縮一下RDD的partition的數量,讓各個partition的數據比較緊湊一些。
也能提升一些性能。

解決各種序列化導致的錯誤

你會看到什麼樣的序列化導致的報錯?

用client模式去提交spark作業,觀察本地打印出來的log。如果出現了類似於Serializable、
Serialize等等字眼,報錯的log,那麼恭喜大家,就碰到了序列化問題導致的報錯。

雖然是報錯,但是序列化報錯,應該是屬於比較簡單的了,很好處理。

序列化報錯要注意的點:

1、你的算子函數裏面,如果使用到了外部的自定義類型的變量,那麼此時,就要求你的自定義類型,
必須是可序列化的。

2、如果要將自定義的類型,作爲RDD的元素類型,那麼自定義的類型也必須是可以序列化的

JavaPairRDD

解決yarn-cluster模式的JVM棧內存溢出問題

實踐經驗,碰到的yarn-cluster的問題:

有的時候,運行一些包含了spark sql的spark作業,可能會碰到yarn-client模式下,可以正常提交運行;
yarn-cluster模式下,可能是無法提交運行的,會報出JVM的PermGen(永久代)的內存溢出,OOM。
JVM裏面的一個區域,就是會放Class裏面一些字符串常量這些東西的。

yarn-client模式下,driver是運行在本地機器上的,spark使用的JVM的PermGen的配置,
是本地的spark-class文件(spark客戶端是默認有配置的),JVM的永久代的大小是128M,
這個是沒有問題的;但是呢,在yarn-cluster模式下,driver是運行在yarn集羣的某個節點上的,
使用的是沒有經過配置的默認設置(PermGen永久代大小),82M。

spark-sql,它的內部是要進行很複雜的SQL的語義解析、語法樹的轉換等等,特別複雜,
在這種複雜的情況下,如果說你的sql本身特別複雜的話,很可能會比較導致性能的消耗,內存的消耗。
可能對PermGen永久代的佔用會比較大。

所以,此時,如果對永久代的佔用需求,超過了82M的話,但是呢又在128M以內;就會出現如上所述的問題,
yarn-client模式下,默認是128M,這個還能運行;如果在yarn-cluster模式下,默認是82M,就有問題了。
會報出PermGen Out of Memory error log。

如何解決這種問題?

既然是JVM的PermGen永久代內存溢出,那麼就是內存不夠用。咱們呢,就給yarn-cluster模式下的,
driver的PermGen多設置一些。

spark-submit腳本中,加入以下配置即可:
–conf spark.driver.extraJavaOptions=”-XX:PermSize=128M -XX:MaxPermSize=256M”

這個就設置了driver永久代的大小,默認是128M,最大是256M。那麼,這樣的話,
就可以基本保證你的spark作業不會出現上述的yarn-cluster模式導致的永久代內存溢出的問題。

多講一句,可能還有一個問題
spark sql,sql,要注意,一個問題

sql,有大量的or語句。比如where keywords=” or keywords=” or keywords=”
當達到or語句,有成百上千的時候,此時可能就會出現一個driver端的jvm stack overflow,
JVM棧內存溢出的問題

JVM棧內存溢出,基本上就是由於調用的方法層級過多,因爲產生了大量的,非常深的,
超出了JVM棧深度限制的,遞歸。遞歸方法。我們的猜測,spark sql,有大量or語句的時候,
spark sql內部源碼中,在解析sql,比如轉換成語法樹,或者進行執行計劃的生成的時候,
對or的處理是遞歸。or特別多的話,就會發生大量的遞歸。

JVM Stack Memory Overflow,棧內存溢出。

這種時候,建議不要搞那麼複雜的spark sql語句。採用替代方案:將一條sql語句,
拆解成多條sql語句來執行。每條sql語句,就只有100個or子句以內;一條一條SQL語句來執行。
根據生產環境經驗的測試,一條sql語句,100個or子句以內,是還可以的。通常情況下,
不會報那個棧內存溢出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章