大數據面試題:大數據性能調優之分配更多資源
分配更多資源:性能調優的王道,就是增長和分配更多的資源,性能和速度上的提高,是顯而易見的;基本上,在必定範圍以內,增長資源與性能的提高,是成正比的;寫完了一個複雜的spark做業以後,進行性能調優的時候,首先第一步,我以爲,就是要來調節最優的資源配置;在這個基礎之上,若是說你的spark做業,可以分配的資源達到了你的能力範圍的頂端以後,沒法再分配更多的資源了,公司資源有限;那麼纔是考慮去作後面的這些性能調優的點。
問題:
一、分配哪些資源?
二、在哪裏分配這些資源?
三、爲何多分配了這些資源之後,性能會獲得提高?
答案:
一、分配哪些資源?executor、cpu per executor、memory per executor、driver memory
二、在哪裏分配這些資源?在咱們在生產環境中,提交spark做業時,用的spark-submit shell腳本,裏面調整對應的參數
/usr/local/spark/bin/spark-submit --class cn.spark.sparktest.core.WordCountCluster --num-executors 80 配置executor的數量
--driver-memory1024m 配置driver的內存(影響不大)
--executor-memory 512m 配置每一個executor的內存大小
--executor-cores 3 配置每一個executor的cpu core數量
/usr/local/SparkTest-0.0.1-SNAPSHOT-jar-with-dependencies.jar 三、調節到多大,算是最大呢?
第一種,Spark Standalone,公司集羣上,搭建了一套Spark集羣,你內心應該清楚每臺機器還可以給你使用的,大概有多少內存,多少cpu core;那麼,設置的時候,就根據這個實際的狀況,去調節每一個spark做業的資源分配。好比說你的每臺機器可以給你使用4G內存,2個cpu core;20臺機器;executor,20;4G內存,2個cpu core,平均每一個executor。
第二種,Yarn。資源隊列。資源調度。應該去查看,你的spark做業,要提交到的資源隊列,大概有多少資源?500G內存,100個cpu core;executor,50;10G內存,2個cpu core,平均每一個executor。
一個原則,你能使用的資源有多大,就儘可能去調節到最大的大小(executor的數量,幾十個到上百個不等;executor內存;executor cpu core)
四、爲何調節了資源之後,性能能夠提高?面試
性能調優之調節spark並行度算法
並行度:其實就是指的是,Spark做業中,各個stage的task數量,也就表明了Spark做業的在各個階段(stage)的並行度。
若是不調節並行度,致使並行度太低,會怎麼樣?
假設,如今已經在spark-submit腳本里面,給咱們的spark做業分配了足夠多的資源,好比50個executor,每一個executor有10G內存,每一個executor有3個cpu core。基本已經達到了集羣或者yarn隊列的資源上限。
task沒有設置,或者設置的不多,好比就設置了,100個task。50個executor,每一個executor有3個cpu core,也就是說,你的Application任何一個stage運行的時候,都有總數在150個cpu core,能夠並行運行。可是你如今,只有100個task,平均分配一下,每一個executor分配到2個task,ok,那麼同時在運行的task,只有100個,每一個executor只會並行運行2個task。每一個executor剩下的一個cpu core,就浪費掉了。
你的資源雖然分配足夠了,可是問題是,並行度沒有與資源相匹配,致使你分配下去的資源都浪費掉了。
合理的並行度的設置,應該是要設置的足夠大,大到能夠徹底合理的利用你的集羣資源;好比上面的例子,總共集羣有150個cpu core,能夠並行運行150個task。那麼就應該將你的Application的並行度,至少設置成150,才能徹底有效的利用你的集羣資源,讓150個task,並行執行;並且task增長到150個之後,便可以同時並行運行,還可讓每一個task要處理的數據量變少;好比總共150G的數據要處理,若是是100個task,每一個task計算1.5G的數據;如今增長到150個task,能夠並行運行,並且每一個task主要處理1G的數據就能夠。
很簡單的道理,只要合理設置並行度,就能夠徹底充分利用你的集羣計算資源,而且減小每一個task要處理的數據量,最終,就是提高你的整個Spark做業的性能和運行速度。sql
一、task數量,至少設置成與Spark application的總cpu core數量相同(最理想狀況,好比總共150個cpu core,分配了150個task,一塊兒運行,差很少同一時間運行完畢)
二、官方是推薦,task數量,設置成spark application總cpu core數量的2~3倍,好比150個cpu core,基本要設置task數量爲300~500;
實際狀況,與理想狀況不一樣的,有些task會運行的快一點,好比50s就完了,有些task,可能會慢一點,要1分半才運行完,因此若是你的task數量,恰好設置的跟cpu core數量相同,可能仍是會致使資源的浪費,由於,好比150個task,10個先運行完了,剩餘140個還在運行,可是這個時候,有10個cpu core就空閒出來了,就致使了浪費。那若是task數量設置成cpu core總數的2~3倍,那麼一個task運行完了之後,另外一個task立刻能夠補上來,就儘可能讓cpu core不要空閒,同時也是儘可能提高spark做業運行的效率和速度,提高性能。
三、如何設置一個Spark Application的並行度?
spark.default.parallelism
SparkConf conf = new SparkConf()
.set("spark.default.parallelism", "300-450")shell
性能調優之RDD架構重構apache
第一,RDD架構重構與優化
儘可能去複用RDD,差很少的RDD,能夠抽取稱爲一個共同的RDD,供後面的RDD計算時,反覆使用。
第二,公共RDD必定要實現持久化
對於要屢次計算和使用的公共RDD,必定要進行持久化。
持久化,也就是說,將RDD的數據緩存到內存中/磁盤中,(BlockManager),之後不管對這個RDD作多少次計算,那麼都是直接取這個RDD的持久化的數據,好比從內存中或者磁盤中,直接提取一份數據。
第三,持久化,是能夠進行序列化的
若是正常將數據持久化在內存中,那麼可能會致使內存的佔用過大,這樣的話,也許,會致使OOM內存溢出。
當純內存沒法支撐公共RDD數據徹底存放的時候,就優先考慮,使用序列化的方式在純內存中存儲。將RDD的每一個partition的數據,序列化成一個大的字節數組,就一個對象;序列化後,大大減小內存的空間佔用。
序列化的方式,惟一的缺點就是,在獲取數據的時候,須要反序列化。
若是序列化純內存方式,仍是致使OOM,內存溢出;就只能考慮磁盤的方式,
內存+磁盤的普通方式(無序列化)。
內存+磁盤,序列化
第四,爲了數據的高可靠性,並且內存充足,可使用雙副本機制,進行持久化
持久化的雙副本機制,持久化後的一個副本,由於機器宕機了,副本丟了,就仍是得從新計算一次;持久化的每一個數據單元,存儲一份副本,放在其餘節點上面;從而進行容錯;一個副本丟了,不用從新計算,還可使用另一份副本。
這種方式,僅僅針對你的內存資源極度充足數組
Spark性能調優之廣播大變量。緩存
這種默認的,task執行的算子中,使用了外部的變量,每一個task都會獲取一份變量的副本,有什麼缺點呢?在什麼狀況下,會出現性能上的惡劣的影響呢?
map,自己是不小,存放數據的一個單位是Entry,還有可能會用鏈表的格式的來存放Entry鏈條。因此map是比較消耗內存的數據格式。
好比,map是1M。總共,你前面調優都調的特好,資源給的到位,配合着資源,並行度調節的絕對到位,1000個task。大量task的確都在並行運行。
這些task裏面都用到了佔用1M內存的map,那麼首先,map會拷貝1000份副本,經過網絡傳輸到各個task中去,給task使用。總計有1G的數據,會經過網絡傳輸。網絡傳輸的開銷,不容樂觀啊!!!網絡傳輸,也許就會消耗掉你的spark做業運行的總時間的一小部分。
map副本,傳輸到了各個task上以後,是要佔用內存的。1個map的確不大,1M;1000個map分佈在你的集羣中,一會兒就耗費掉1G的內存。對性能會有什麼影響呢?
沒必要要的內存的消耗和佔用,就致使了,你在進行RDD持久化到內存,也許就無法徹底在內存中放下;就只能寫入磁盤,最後致使後續的操做在磁盤IO上消耗性能;
你的task在建立對象的時候,也許會發現堆內存放不下全部對象,也許就會致使頻繁的垃圾回收器的回收,GC。GC的時候,必定是會致使工做線程中止,也就是致使Spark暫停工做那麼一點時間。頻繁GC的話,對Spark做業的運行的速度會有至關可觀的影響。網絡
大數據面試題,spark性能調優之使用kryo序列化架構
默認狀況下,Spark內部是使用Java的序列化機制,ObjectOutputStream / ObjectInputStream,對象輸入輸出流機制,來進行序列化
這種默認序列化機制的好處在於,處理起來比較方便;也不須要咱們手動去作什麼事情,只是,你在算子裏面使用的變量,必須是實現Serializable接口的,可序列化便可。
可是缺點在於,默認的序列化機制的效率不高,序列化的速度比較慢;序列化之後的數據,佔用的內存空間相對仍是比較大。
能夠手動進行序列化格式的優化
Spark支持使用Kryo序列化機制。Kryo序列化機制,比默認的Java序列化機制,速度要快,序列化後的數據要更小,大概是Java序列化機制的1/10。
因此Kryo序列化優化之後,可讓網絡傳輸的數據變少;在集羣中耗費的內存資源大大減小。
Kryo序列化機制,一旦啓用之後,會生效的幾個地方:
一、算子函數中使用到的外部變量
二、持久化RDD時進行序列化,StorageLevel.MEMORY_ONLY_SER
三、shuffle
一、算子函數中使用到的外部變量,使用Kryo之後:優化網絡傳輸的性能,能夠優化集羣中內存的佔用和消耗
二、持久化RDD,優化內存的佔用和消耗;持久化RDD佔用的內存越少,task執行的時候,建立的對象,就不至於頻繁的佔滿內存,頻繁發生GC。
三、shuffle:能夠優化網絡傳輸的性能
實現:
SparkConf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
首先第一步,在SparkConf中設置一個屬性,spark.serializer,org.apache.spark.serializer.KryoSerializer類;
Kryo之因此沒有被做爲默認的序列化類庫的緣由,就要出現了:主要是由於Kryo要求,若是要達到它的最佳性能的話,那麼就必定要註冊你自定義的類(好比,你的算子函數中使用到了外部自定義類型的對象變量,這時,就要求必須註冊你的類,不然Kryo達不到最佳性能)。
第二步,註冊你使用到的,須要經過Kryo序列化的,一些自定義類,SparkConf.registerKryoClasses()
項目中的使用:
.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
.registerKryoClasses(new Class[]{CategorySortKey.class})app
大數據面試題:jvm調優之原理概述以及下降cache操做的內存佔比
每一次放對象的時候,都是放入eden區域,和其中一個survivor區域;另一個survivor區域是空閒的。
當eden區域和一個survivor區域放滿了之後(spark運行過程當中,產生的對象實在太多了),就會觸發minor 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頻繁的中止工做。性能影響會很大。
那麼就經過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
大數據面試題:shuffle調優之合併map端輸出文件
開啓了map端輸出文件的合併機制以後:
第一個stage,同時就運行cpu core個task,好比cpu core是2個,並行運行2個task;每一個task都建立下一個stage的task數量個文件;
第一個stage,並行運行的2個task執行完之後;就會執行另外兩個task;另外2個task不會再從新建立輸出文件;而是複用以前的task建立的map端輸出文件,將數據寫入上一批task的輸出文件中。
第二個stage,task在拉取數據的時候,就不會去拉取上一個stage每個task爲本身建立的那份輸出文件了;而是拉取少許的輸出文件,每一個輸出文件中,可能包含了多個task給本身的map端輸出。
提醒一下(map端輸出文件合併):
只有並行執行的task會去建立新的輸出文件;下一批並行執行的task,就會去複用以前已有的輸出文件;可是有一個例外,好比2個task並行在執行,可是此時又啓動要執行2個task;那麼這個時候的話,就沒法去複用剛纔的2個task建立的輸出文件了;而是仍是隻能去建立新的輸出文件。
要實現輸出文件的合併的效果,必須是一批task先執行,而後下一批task再執行,才能複用以前的輸出文件;負責多批task同時起來執行,仍是作不到複用的。
大數據面試題:shuffle調優之調節map端內存緩衝與reduce端內存佔比
spark.shuffle.file.buffer,默認32k
spark.shuffle.memoryFraction,0.2
reduce端task,在拉取到數據以後,會用hashmap的數據格式,來對各個key對應的values進行匯聚。
針對每一個key對應的values,執行咱們自定義的聚合函數的代碼,好比_ + _(把全部values累加起來)
reduce task,在進行匯聚、聚合等操做的時候,實際上,使用的就是本身對應的executor的內存,executor(jvm進程,堆),默認executor內存中劃分給reduce task進行聚合的比例,是0.2。
問題來了,由於比例是0.2,因此,理論上,頗有可能會出現,拉取過來的數據不少,那麼在內存中,放不下;這個時候,默認的行爲,就是說,將在內存放不下的數據,都spill(溢寫)到磁盤文件中去。
原理說完以後,來看一下,默認狀況下,不調優,可能會出現什麼樣的問題?
默認,map端內存緩衝是每一個task,32kb。
默認,reduce端聚合內存比例,是0.2,也就是20%。
若是map端的task,處理的數據量比較大,可是呢,你的內存緩衝大小是固定的。可能會出現什麼樣的狀況?
每一個task就處理320kb,32kb,總共會向磁盤溢寫320 / 32 = 10次。
每一個task處理32000kb,32kb,總共會向磁盤溢寫32000 / 32 = 1000次。
在map task處理的數據量比較大的狀況下,而你的task的內存緩衝默認是比較小的,32kb。可能會形成屢次的map端往磁盤文件的spill溢寫操做,發生大量的磁盤IO,從而下降性能。
reduce端聚合內存,佔比。默認是0.2。若是數據量比較大,reduce task拉取過來的數據不少,那麼就會頻繁發生reduce端聚合內存不夠用,頻繁發生spill操做,溢寫到磁盤上去。並且最要命的是,磁盤上溢寫的數據量越大,後面在進行聚合操做的時候,極可能會屢次讀取磁盤中的數據,進行聚合。
默認不調優,在數據量比較大的狀況下,可能頻繁地發生reduce端的磁盤文件的讀寫。
這兩個點之因此放在一塊兒講,是由於他們倆是有關聯的。數據量變大,map端確定會出點問題;reduce端確定也會出點問題;出的問題是同樣的,都是磁盤IO頻繁,變多,影響性能。
調優:
在實際生產環境中,咱們在何時來調節兩個參數?
若是發現shuffle 磁盤的write和read,很大。這個時候,就意味着最好調節一些shuffle的參數。進行調優。首先固然是考慮開啓map端輸出文件合併機制。
調節上面說的那兩個參數。調節的時候的原則。spark.shuffle.file.buffer,每次擴大一倍,而後看看效果,64,128;spark.shuffle.memoryFraction,每次提升0.1,看看效果。
調節了之後,效果?map task內存緩衝變大了,減小spill到磁盤文件的次數;reduce端聚合內存變大了,減小spill到磁盤的次數,並且減小了後面聚合讀取磁盤文件的數量。
大數據面試題:spark調優troubleshooting之控制shuffle reduce端緩存大小避免OOM
map端的task是不斷的輸出數據的,數據量多是很大的。
可是,其實reduce端的task,並非等到map端task將屬於本身的那份數據所有寫入磁盤文件以後,再去拉取的。map端寫一點數據,reduce端task就會拉取一小部分數據,當即進行後面的聚合、算子函數的應用。
每次reduece可以拉取多少數據,就由buffer來決定。由於拉取過來的數據,都是先放在buffer中的。而後才用後面的executor分配的堆內存佔比(0.2),hashmap,去進行後續的聚合、函數的執行。
reduce端緩衝(buffer),可能會出什麼問題?
多是會出現,默認是48MB,也許大多數時候,reduce端task一邊拉取一邊計算,不必定一直都會拉滿48M的數據。可能大多數時候,拉取個10M數據,就計算掉了。
大多數時候,也許不會出現什麼問題。可是有的時候,map端的數據量特別大,而後寫出的速度特別快。reduce端全部task,拉取的時候,所有達到本身的緩衝的最大極限值,緩衝,48M,所有填滿。
這個時候,再加上你的reduce端執行的聚合函數的代碼,可能會建立大量的對象。也許,一會兒,內存就撐不住了,就會OOM。reduce端的內存中,就會發生內存溢出的問題。
針對上述的可能出現的問題,咱們該怎麼來解決呢?
這個時候,就應該減小reduce端task緩衝的大小。我寧願多拉取幾回,可是每次同時可以拉取到reduce端每一個task的數量,比較少,就不容易發生OOM內存溢出的問題。(好比,能夠調節成12M)
在實際生產環境中,咱們都是碰到過這種問題的。這是典型的以性能換執行的原理。reduce端緩衝小了,不容易OOM了,可是,性能必定是有所降低的,你要拉取的次數就多了。就走更多的網絡傳輸開銷。
這種時候,只能採起犧牲性能的方式了,spark做業,首先,第一要義,就是必定要讓它能夠跑起來。而後纔去考慮性能的調優。
再來講說,reduce端緩衝大小的另一面,關於性能調優的一面:
我們假如說,你的Map端輸出的數據量也不是特別大,而後你的整個application的資源也特別充足。200個executor、5個cpu core、10G內存。
其實能夠嘗試去增長這個reduce端緩衝大小的,好比從48M,變成96M。那麼這樣的話,每次reduce task可以拉取的數據量就很大。須要拉取的次數也就變少了。好比原先須要拉取100次,如今只要拉取50次就能夠執行完了。
對網絡傳輸性能開銷的減小,以及reduce端聚合操做執行的次數的減小,都是有幫助的。
最終達到的效果,就應該是性能上的必定程度上的提高。
必定要注意,資源足夠的時候,再去作這個事兒。
調節參數爲
spark.reducer.maxSizeInFlight,48//調小變爲
spark.reducer.maxSizeInFlight,24
大數據面試題:spark調優troubleshooting之解決JVM GC致使的shuffle文件拉取失敗
有時會出現的一種狀況,很是廣泛,在spark的做業中;shuffle file not found。(spark做業中,很是很是常見的)並且,有的時候,它是偶爾纔會出現的一種狀況。有的時候,出現這種狀況之後,會從新去提交stage、task。從新執行一遍,發現就行了。沒有這種錯誤了。
log怎麼看?用client模式去提交你的spark做業。好比standalone client;yarn client。一提交做業,直接能夠在本地看到刷刷刷更新的log。
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,沒法拉取到的問題。
大數據面試題:spark調優troubleshooting之解決yarn-cluster模式的jvm內存溢出沒法執行問題
實踐經驗,碰到的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模式致使的永久代內存溢出的問題。
大數據面試題:spark調優troubleshooting之checkpoint的使用
持久化,大多數時候,都是會正常工做的。可是就怕,有些時候,會出現意外。 好比說,緩存在內存中的數據,可能莫名其妙就丟失掉了。 或者說,存儲在磁盤文件中的數據,莫名其妙就沒了,文件被誤刪了。 出現上述狀況的時候,接下來,若是要對這個RDD執行某些操做,可能會發現RDD的某個partition找不到了。 對消失的partition從新計算,計算完之後再緩存和使用。 有些時候,計算某個RDD,多是極其耗時的。可能RDD以前有大量的父RDD。那麼若是你要從新計算一個partition,可能要從新計算以前全部的父RDD對應的partition。 這種狀況下,就能夠選擇對這個RDD進行checkpoint,以防萬一。進行checkpoint,就是說,會將RDD的數據,持久化一份到容錯的文件系統上(好比hdfs)。 在對這個RDD進行計算的時候,若是發現它的緩存數據不見了。優先就是先找一下有沒有checkpoint數據(到hdfs上面去找)。若是有的話,就使用checkpoint數據了。不至於說是去從新計算。 checkpoint,其實就是能夠做爲是cache的一個備胎。若是cache失效了,checkpoint就能夠上來使用了。 checkpoint有利有弊,利在於,提升了spark做業的可靠性,一旦發生問題,仍是很可靠的,不用從新計算大量的rdd;可是弊在於,進行checkpoint操做的時候,也就是將rdd數據寫入hdfs中的時候,仍是會消耗性能的。 checkpoint,用性能換可靠性。 checkpoint原理: 一、在代碼中,用SparkContext,設置一個checkpoint目錄,能夠是一個容錯文件系統的目錄,好比hdfs; 二、在代碼中,對須要進行checkpoint的rdd,執行RDD.checkpoint(); 三、RDDCheckpointData(spark內部的API),接管你的RDD,會標記爲marked for checkpoint,準備進行checkpoint 四、你的job運行完以後,會調用一個finalRDD.doCheckpoint()方法,會順着rdd lineage,回溯掃描,發現有標記爲待checkpoint的rdd,就會進行二次標記,inProgressCheckpoint,正在接受checkpoint操做 五、job執行完以後,就會啓動一個內部的新job,去將標記爲inProgressCheckpoint的rdd的數據,都寫入hdfs文件中。(備註,若是rdd以前cache過,會直接從緩存中獲取數據,寫入hdfs中;若是沒有cache過,那麼就會從新計算一遍這個rdd,再checkpoint) 六、將checkpoint過的rdd以前的依賴rdd,改爲一個CheckpointRDD*,強制改變你的rdd的lineage。後面若是rdd的cache數據獲取失敗,直接會經過它的上游CheckpointRDD,去容錯的文件系統,好比hdfs,中,獲取checkpoint的數據。