其餘更多java基礎文章:
java基礎學習(目錄)java
學習資料:
Spark面對OOM問題的解決方法及優化總結sql
每次reduece可以拉取多少數據,就由buffer來決定。由於拉取過來的數據,都是先放在buffer中的。而後才用後面的executor分配的堆內存佔比(0.2),去進行後續的聚合、函數的執行。緩存
緩存默認是48MB,也許大多數時候,reduce端task一邊拉取一邊計算,不必定一直都會拉滿48M的數據。可能大多數時候,拉取個10M數據,就計算掉了。bash
大多數時候,也許不會出現什麼問題。可是有的時候,map端的數據量特別大,而後寫出的速度特別快。reduce端全部task,拉取的時候,所有達到本身的緩衝的最大極限值,緩衝,48M,所有填滿。這個時候,再加上你的reduce端執行的聚合函數的代碼,可能會建立大量的對象。也許,一會兒,內存就撐不住了,就會OOM。reduce端的內存中,就會發生內存溢出的問題。網絡
這個時候,就應該減小reduce端task緩衝的大小。我寧願多拉取幾回,可是每次同時可以拉取到reduce端每一個task的數量,比較少,就不容易發生OOM內存溢出的問題。(好比,能夠調節成12M)運維
有時會出現的一種狀況,很是廣泛,在spark的做業中;shuffle file not found。(spark做業中,很是很是常見的)並且,有的時候,它是偶爾纔會出現的一種狀況。有的時候,出現這種狀況之後,會從新去提交stage、task。從新執行一遍,發現就行了。沒有這種錯誤了。函數
好比,executor的JVM進程,可能內存不是很夠用了。那麼此時可能就會執行GC。minor GC or full GC。總之一旦發生了JVM以後,就會致使executor內,全部的工做線程所有中止,好比BlockManager,基於netty的網絡通訊。post
下一個stage的executor,多是尚未中止掉的,task想要去上一個stage的task所在的exeuctor,去拉取屬於本身的數據,結果因爲對方正在gc,就致使拉取了半天沒有拉取到。性能
就極可能會報出,shuffle file not found。可是,可能下一個stage又從新提交了stage或task之後,再執行就沒有問題了,由於可能第二次就沒有碰到JVM在gc了。學習
解決參數以下:
spark.shuffle.io.maxRetries 3
複製代碼
第一個參數,意思就是說,shuffle文件拉取的時候,若是沒有拉取到(拉取失敗),最多或重試幾回(會從新拉取幾回文件),默認是3次。
spark.shuffle.io.retryWait 5s
複製代碼
第二個參數,意思就是說,每一次重試拉取文件的時間間隔,默認是5s鍾。
默認狀況下,假如說第一個stage的executor正在進行漫長的full gc。第二個stage的executor嘗試去拉取文件,結果沒有拉取到,默認狀況下,會反覆重試拉取3次,每次間隔是五秒鐘。最多隻會等待3 * 5s = 15s。若是15s內,沒有拉取到shuffle file。就會報出shuffle file not found。
針對這種狀況,咱們徹底能夠進行預備性的參數調節。增大上述兩個參數的值,達到比較大的一個值,儘可能保證第二個stage的task,必定可以拉取到上一個stage的輸出文件。避免報shuffle file not found。而後可能會從新提交stage和task去執行。那樣反而對性能也很差。
spark.shuffle.io.maxRetries 60
spark.shuffle.io.retryWait 60s
複製代碼
最多能夠忍受1個小時沒有拉取到shuffle file。只是去設置一個最大的可能的值。full gc不可能1個小時都沒結束吧。
這樣呢,就能夠儘可能避免由於gc致使的shuffle file not found,沒法拉取到的問題。
用client模式去提交spark做業,觀察本地打印出來的log。若是出現了相似於Serializable、Serialize等等字眼,報錯的log,就是碰到了序列化問題致使的報錯。雖然是報錯,可是序列化報錯,應該是屬於比較簡單的了,很好處理。
序列化報錯要注意的三個點:
final Teacher teacher = new Teacher("leo");
studentsRDD.foreach(new VoidFunction() {
public void call(Row row) throws Exception {
String teacherName = teacher.getName();
....
}
});
public class Teacher implements Serializable {
}
複製代碼
JavaPairRDD<Integer, Teacher> teacherRDD
JavaPairRDD<Integer, Student> studentRDD
studentRDD.join(teacherRDD)
public class Teacher implements Serializable {
}
public class Student implements Serializable {
}
複製代碼
Connection conn =
studentsRDD.foreach(new VoidFunction() {
public void call(Row row) throws Exception {
conn.....
}
});
複製代碼
Connection是不支持序列化的
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-cluster的問題:
有的時候,運行一些包含了spark sql的spark做業,可能會碰到yarn-client模式下,能夠正常提交運行;yarn-cluster模式下,多是沒法提交運行的,會報出JVM的PermGen(永久代)的內存溢出,OOM。
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模式致使的永久代內存溢出的問題。