Spark JVM調優

對於JVM調優,首先應該明確,(major)full gc/minor gc,都會導致JVM的工作線程停止工作,即stop the world。

1 JVM調優一:降低cache操作的內存佔比

1.靜態內存管理機制

根據Spark靜態內存管理機制,堆內存被劃分為了兩塊,Storage和Execution。Storage主要用於緩存RDD數據和broadcast數據,Execution主要用於緩存在shuffle過程中產生的中間數據,Storage佔系統內存的60%,Execution佔系統內存的20%,並且兩者完全獨立。

在一般情況下,Storage的內存都提供給了cache操作,但是如果在某些情況下cache操作內存不是很緊張,而task的算子中創建的對象很多,Execution內存又相對較小,這回導致頻繁的minor gc,甚至於頻繁的full gc,進而導致Spark頻繁的停止工作,性能影響會很大。

在Spark UI中可以查看每個stage的運行情況,包括每個task的運行時間、gc時間等等,如果發現gc太頻繁,時間太長,就可以考慮調節Storage的內存佔比,讓task執行算子函數式,有更多的內存可以使用。

Storage內存區域可以通過spark.storage.memoryFraction參數進行指定,默認為0.6,即60%,可以逐級向下遞減,如代碼清單2-6所示:

代碼清單2-6 Storage內存佔比設置

val conf = new SparkConf().set("spark.storage.memoryFraction", "0.4") 
  1. 統一內存管理機制

根據Spark統一內存管理機制,堆內存被劃分為了兩塊,Storage和Execution。Storage主要用於緩存數據,Execution主要用於緩存在shuffle過程中產生的中間數據,兩者所組成的內存部分稱為統一內存,Storage和Execution各佔統一內存的50%,由於動態佔用機制的實現,shuffle過程需要的內存過大時,會自動佔用Storage的內存區域,因此無需手動進行調節。

2 JVM調優二:調節Executor堆外內存

Executor的堆外內存主要用於程序的共享庫、Perm Space、 線程Stack和一些Memory mapping等, 或者類C方式allocate object。

有時,如果你的Spark作業處理的數據量非常大,達到幾億的數據量,此時運行Spark作業會時不時地報錯,例如shuffle output file cannot find,executor lost,task lost,out of memory等,這可能是Executor的堆外內存不太夠用,導致Executor在運行的過程中內存溢出。

stage的task在運行的時候,可能要從一些Executor中去拉取shuffle map output文件,但是Executor可能已經由於內存溢出掛掉了,其關聯的BlockManager也沒有了,這就可能會報出shuffle output file cannot find,executor lost,task lost,out of memory等錯誤,此時,就可以考慮調節一下Executor的堆外內存,也就可以避免報錯,與此同時,堆外內存調節的比較大的時候,對於性能來講,也會帶來一定的提升。

默認情況下,Executor堆外內存上限大概為300多MB,在實際的生產環境下,對海量數據進行處理的時候,這裡都會出現問題,導致Spark作業反覆崩潰,無法運行,此時就會去調節這個參數,到至少1G,甚至於2G、4G。

Executor堆外內存的配置需要在spark-submit腳本里配置,如代碼清單2-7所示:

代碼清單2-7 Executor堆外內存配置

--conf spark.yarn.executor.memoryOverhead=2048

以上參數配置完成後,會避免掉某些JVM OOM的異常問題,同時,可以提升整體Spark作業的性能。

3 JVM調優三:調節連接等待時長

在Spark作業運行過程中,Executor優先從自己本地關聯的BlockManager中獲取某份數據,如果本地BlockManager沒有的話,會通過TransferService遠程連接其他節點上Executor的BlockManager來獲取數據。

如果task在運行過程中創建大量對象或者創建的對象較大,會佔用大量的內存,這回導致頻繁的垃圾回收,但是垃圾回收會導致工作現場全部停止,也就是說,垃圾回收一旦執行,Spark的Executor進程就會停止工作,無法提供相應,此時,由於沒有響應,無法建立網絡連接,會導致網絡連接超時。

在生產環境下,有時會遇到file not found、file lost這類錯誤,在這種情況下,很有可能是Executor的BlockManager在拉取數據的時候,無法建立連接,然後超過默認的連接等待時長60s後,宣告數據拉取失敗,如果反覆嘗試都拉取不到數據,可能會導致Spark作業的崩潰。這種情況也可能會導致DAGScheduler反覆提交幾次stage,TaskScheduler反覆提交幾次task,大大延長了我們的Spark作業的運行時間。

此時,可以考慮調節連接的超時時長,連接等待時長需要在spark-submit腳本中進行設置,設置方式如代碼清單2-8所示:

代碼清單2-8 連接等待時長配置

--conf spark.core.connection.ack.wait.timeout=300

調節連接等待時長後,通常可以避免部分的XX文件拉取失敗、XX文件lost等報錯。


分享到:


相關文章: