Spark_常規性能調優(二)
1、優先使用數組以及字符串,而不是集合類。也就是說,優先使用array,而不是ArrayList、LinkedList、HashMap等集合。
比如:企業應用中的做法是,對于對于HashMap、List這種數據結構,統一用String拼接成特殊格式的字符串,比如Map
2、避免使用多層嵌套的對象結構。
比如:public class Teacher{private List
優化:我們使用特殊的字符串來進行數據的存儲。比如,用json字符串來存儲數據,就是一個非常好的選擇。
{“teacher”:1,”tracherName”:”leo”,students:[{“studentid”:1,”studentName”:”tome”}]}。
3、對于有些能夠避免的場景,盡量使用int替代String,雖然String比ArrayList、HashMap等數據結構高效多,占用內存量少,但是,還有會有額外的信息消耗,就想對于id這樣的字段,我們完全可以使用int代替。
由于RDD的數據是持久化到內存,或者是磁盤中的,此時,如果內存大小不是特別充足,完全可以使用序列化的持久化級別,比如:MEMORY_ONLY_SER、MEMORY_AND_DISK_SER等這種帶ser的就代表序列化持久化級別,使用方式為:
RDD.persist(StorageLevel.MEMORY_ONLY_SER)這樣的語法即可。 這樣是將數據序列化然后在持久化,大大的減小對內存的消耗。
答:當我們有兩個Executor,每個Executor都有五個cpu core,
當我們設置參數new SparkConf().set(“spark.default.parallelism”,”5”)
這個參數的意思就是,把所有的RDD的partition都設置成5,也就是說明每個RDD的數據都會被分成五份。那么針對RDD的partition,一個partition就會啟動一個線程task來進行計算。但是在集群中,我們分明分配了十個cpu core,但是只會使用五個task,使用五個cpu core,那么剩下的五個就會被浪費掉。
優化:我們完全可以設置相同的task,讓十個cpu core都在運行,甚至設置20-30個,因為每個task運行結束的時候不一樣,可能某個task很快就完成了,那么cpu又空閑了。
官網建議,設置集群總cpu數量的兩倍~~三倍的并行度,這樣的話,每個cpu core可能分配到并發運行 2 ~ 3 個task線程,那么這樣就會連續、運轉、最大的發揮他的功效來。
舉例:當我們的spark-submit設置了executor的數量是10個,每個executor要求分配2個core,那么application總共會有20個core,此時我們可以設置:
new SparkConf().set(“spark.default.parallelism”,”60”)合理設置并行度。
答:數據本地化:指的是,數據離計算它的代碼有多近,也就是我們要計算的數據和執行代碼節點之間的距離。
(1)、PROCESS_LOCAL:數據和計算它的代碼在同一個JVM進程中。
(2)、NODE_LOCAL:數據和計算它的代碼在一個節點上,但是不在一個進程中,比如在不同的executor進程中,或者是數據在HDFS文件的block中。
(3)、NO_PREF:數據從哪里過來,性能都是一樣的。
(4)、RACK_LOCAL:數據和計算它的代碼在一個機架上。
(5)、ANY:數據可能在任意地方,比如其他網絡環境內,或者其他機架上。
1、 當我們task要處理partition的數據,在某一個Executor中,然后,有TaskSchedulerImpl首先會用最好的本地化級別去啟動task,盡量的在那個包含了要處理的partition的executor中,去啟動task ,就是PROCESS_LOCAL級別。
2、 但是,當那個包含了partition的executor中已經運行了足夠的task 的時候,這個時候,這個計算的task默認的情況下,會等待一會,等待該executor什么時候空閑出來了一個cpu core ,然后這個task來啟動。這個等待是有時間限制的(這個時間限制可以調優),當發現始終沒有空閑的cpu core 的時候,那么就會放大一個級別去啟動這個task。
3、 當然這個task去別的地方計算的時候,會調用RDD的itrator()方法,然后通過executor關聯的blockManager,來嘗試獲取數據。BlocakManager底層,首先嘗試從getLocal()在本地找數據,如果沒有找到,那么就調用getRemote()方法,通過BlockTransferService,鏈接到有數據的BlockManager,來獲取數據。
1、每個Java對象,都有一個對象頭,會占用16個字節,主要是包括了一些對象的元信息,比如指向它的類的指針。如果一個對象本身很小,比如就包括了一個int類型的field,那么它的對象頭實際上比對象自己還要大。
2、Java的String對象,會比它內部的原始數據,要多出40個字節。因為它內部使用char數組來保存內部的字符序列的,并且還得保存諸如數組長度之類的信息。而且因為String使用的是UTF-16編碼,所以每個字符會占用2個字節。比如,包含10個字符的String,會占用60個字節。【結論:一個字符相當于占用6個字節】
3、Java中的集合類型,比如HashMap和LinkedList,內部使用的是鏈表數據結構,所以對鏈表中的每一個數據,都使用了Entry對象來包裝。Entry對象不光有對象頭,還有指向下一個Entry的指針,通常占用8個字節。
4、 元素類型為原始數據類型(比如int)的集合,內部通常會使用原始數據類型的包裝類型,比如Integer,來存儲元素。
1、首先,自己設置RDD的并行度, 有兩種方式,
(1)、在parallelize()、textFile()等方法中,傳入第二個參數,設置RDD的task/partition的數量。
(2)、用SparkConf.set()方法,設置一個參數,spark.default.parallelize,可以統一設置這個application所有的RDD的partition數量。
2、其實,在程序中將RDD cache到內存中,調用RDD.cache()方法即可。
3、最后,觀察Driver的log,就會發現類似于:“INFO BlockManagerMasterActor.Added rdd_0_1 in memory on mbk.local:50311(size:717.5 KB , free: 333.3 MB)”的日志信息,這就顯示了每個partition占用了多少內存。
4、將這個內存信息乘以partition數量,即可得出RDD的內存占用量。
spark 應用性能調優
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。