Spark性能調優之代碼方面的優化
1.避免創建重復的RDD
對性能沒有問題,但會造成代碼混亂
2.盡可能復用同一個RDD,減少產生RDD的個數
3.對多次使用的RDD進行持久化(cache,persist,checkpoint)
如何選擇一種最合適的持久化策略?
默認MEMORY_ONLY, 性能很高, 而且不需要復制一份數據的副本,遠程傳送到其他節點上(BlockManager中的BlockTransferService),但是這里必須要注意的是,在實際的生產環境中,恐怕能夠直接用這種
策略的場景還是有限的,如果RDD中數據比較多時(比如幾十億),直接用這種持久化
級別,會導致JVM的OOM內存溢出異常。
如果使用
MEMORY_ONLY級別時發生了內存溢出,建議嘗試使用MEMORY_ONLY_SER級別,該級別會將RDD數據序列化后再保存在內存中,此時每個
partition僅僅是一個字節數組而已,大大減少了對象數量,並降低了內存占用。這種級別
比MEMORY_ONLY多出來的性能開銷,主要就是序列化與反序列化的開銷。
如果純內存的級別都無法使用,那么建議使用MEMORY_AND_DISK_SER策略,而不是
MEMORY_AND_DISK策略。因為既然到了這一步,就說明RDD的數據量很大,內存無
法完全放下。序列化后的數據比較少,可以節省內存和磁盤的空間開銷。同時該策略會優
先盡量嘗試將數據緩存在內存中,內存緩存不下才會寫入磁盤。
通常不建議使用DISK_ONLY和后綴為_2的級別:
因為完全基於磁盤文件進行數據的讀寫
,會導致性能急劇降低,有時還不如重新計算一次所有RDD。后綴為_2的級別,必須將
所有數據都復制一份副本,並發送到其他節點上,數據復制以及網絡傳輸會導致較大的性
能開銷,除非是要求作業的高可用性,否則不建議使用。
checkpoint 可以使數據安全,切斷依賴關系(如果某一個rdd丟失了,重新計算的鏈太長?)
4.盡量避免使用shuffle類的算子
廣播變量模擬join(一個RDD比較小,另一個RDD比較大
)
5.使用map-side預聚合shuffle操作
reduceByKey aggregateByKey
6.使用高性能的算子
有哪些高性能的算子?
reduceByKey/aggregateByKey 替代 groupByKey
mapPartitions 替代普通map Transformation算子
foreachPartitions 替代 foreach Action算子
repartitionAndSortWithinPartitions 替代repartition與sort類操
作
rdd.partitionBy() //其實自定義一個分區器
repartition
coalesce(numPartitions,true) 增多分區使用這個
coalesce(numPartitions,false) 減少分區 沒有shuffle只是合並
partition
7.廣播變量
開發過程中,會遇到需要在算子函數中使用外部變量的場景(尤其是大變量,比如
100M以上的大集合),那么此時就應該使用Spark的廣播(Broadcast)功能來提
升性能,如果使用的外部變量比較大,建議使用Spark的廣播功能,對該變量進行廣播。廣播
后的變量,會保證每個Executor的內存中,只駐留一份變量副本,而Executor中的
task執行時共享該Executor中的那份變量副本。這樣的話,可以
大大減少變量副本
的數量,從而減少網絡傳輸的性能開銷,並減少對Executor內存的占用開銷,降低
GC的頻率
Executor 2G, 2G*0.48
8.使用kryo序列化性能?
• Spark支持使用Kryo序列化機制。Kryo序列化機制,
比默認的Java序列化機制,速度要快
,序列化后的數據要更小,大概是Java序列化機制的1/10。所以Kryo序列化優化以后,可
以讓網絡傳輸的數據變少;在集群中耗費的內存資源大大減少。
• 對於這三種出現序列化的地方,我們都可以通過使用Kryo序列化類庫,來優化序列化和
反序列化的性能。
Spark默認使用的是Java的序列化機制,也就是
ObjectOutputStream/ObjectInputStream API來進行序列化和反序列化。但是Spark同
時支持使用Kryo序列化庫,Kryo序列化類庫的性能比Java序列化類庫的性能要高很多。
官方介紹,Kryo序列化機制比Java序列化機制,性能高10倍左右。Spark之所以默認沒有
使用Kryo作為序列化類庫,是因為Kryo要求最好要注冊所有需要進行序列化的自定義類
型,因此對於開發者來說,這種方式比較麻煩
自定義的類有哪些,都要注冊
9.優化數據結構
盡量
使用字符串代替對象,
使用原始類型(Int,long)替代字符串,
使用數組替代集合類型,這樣盡可能地
減少內存占用
,從而降低GC頻率,提升性能。
10.使用高性能的fastutil
• fastutil是擴展了Java標准集合框架(Map、List、Set;HashMap、ArrayList、
HashSet)的類庫,提供了特殊類型的map、set、list和queue;
• fastutil能夠提供更小的內存占用,更快的存取速度;我們使用fastutil提供的集合類,來
替代自己平時使用的JDK的原生的Map、List、Set,好處在於,fastutil集合類,可以減
小內存的占用,並且在進行集合的遍歷、根據索引(或者key)獲取元素的值和設置元素
的值的時候,提供更快的存取速度;
•
fastutil最新版本要求Java 7以及以上版本;
•
fastutil的每一種集合類型,都實現了對應的Java中的標准接口(比如fastutil的map,實
現了Java的Map接口),因此可以直接放入已有系統的任何代碼中。
RandomExtractCars
提供了一些集合,性能更高
必須是java7以上的版本
