Spark優化
主要分爲兩個方面的優化,一是代碼邏輯的優化,二是資源配置的優化
1.代碼邏輯
1.1.RDD優化
RDD優化主要也有兩個方面的考慮,一是RDD的複用,二是RDD的持久化。那麼主要針對RDD的持久化進行說明。在Spark中多次對同一個RDD執行算子時,每次都會對這個RDD的父RDD重新計算一次,所以要避免這種重複計算的資源浪費,那麼就需要對RDD進行持久化。
Memory_Only | 內存 |
Memory_Only_2 | 內存+數據備份 |
Memory_Only_Ser |
內存+序列化 |
Memory_And_Disk | 內存+磁盤 |
Memory_And_Disk_2 | 內存+磁盤+備份 |
Memory_And_Disk_Ser | 內存+磁盤+序列化 |
Disk_Only | 磁盤 |
1.2.並行度優化
基本概念:
-
CPU核數
一臺服務器有兩顆CPU,每顆CPU是16核,一顆CPU的超線程數是2,所以:
假設我們的集羣是100臺服務器那麼:
總核數 = 100 * 2 * 16 = 3200
總的邏輯CPU數 = 100 * 2 * 16 * 2 = 6400
-
RDD分區
默認參數:spark.files.maxPartitionBytes = 128 M,就是說當一個文件大小超過128M纔會拆成兩個分區,而當SparkContext創建後會生成兩個參數
sc.defaultParallelism = spark.default.parallelism
sc.defaultMinPartitions = min(spark.default.parallelism,2)
也就是說,我們可以根據這兩個參數來推算RDD的分區數量。當讀取本地文件時:默認RDD分區公式: max(本地file的分片數, sc.defaultMinPartitions)。當讀取HSFS文件時:默認RDD分區公式:max(hdfs文件的block數目, sc.defaultMinPartitions)
Spark並行度:
Spark並行度就是每個Spark作業中各個Stage中所有task的數量,也就表示了Spark作業在各個階段的並行度,需要知道的是DAGScheduler會根據Spark作業中是否存在Shuffle Dependency來劃分Stage,然後爲每個Stage中的Task組成TaskSet交給TaskScheduler,那麼TaskScheduler會將TaskSet發送到Executor進行處理,當Executor接受到TaskSet之後進行反序列化之後把這些Task封裝到TaskRunner的線程中執行。Spark並行度是由參數spark.default.parallelism所控制的,而默認的並行度分爲這麼幾種:
-
本地模式
在local[n]模式下:spark.default.parallelism = n
-
集羣
在Sandalone/Yarn模式下:spark.default.parallelism = 所有Executor數乘以每個Executor的core數,也就是所有Executor總的core數
總結:
比如:集羣規模是100臺,每臺服務器兩顆CPU,每顆CPU16核,那麼總的CPUcores就是6400個,如果在計算的任何stage中使用的並行task的數量沒有足夠多,那麼集羣資源是無法被充分利用。所以爲了充分利用系統資源儘可能提高。
- 參數一:num-executors
該參數用於設置Spark作業總共需要用多少個Executor來執行
- 參數二:executor-cores
該參數用於設置每個Executor進程的CPU core數量
- 參數三:spark.default.parallelism
該參數用於設置每個Stage默認的Task數量
補充:
每個幾點可以啓動一個或多個Executor,每個Executor由一個或多個core組成,每個core只能執行一個task,每個task執行的結果產生一個目標partition
1.3.廣播變量
廣播變量其實就是將ReduceJoin轉化爲MapJoin,這樣做的好處就是將Reduce端每個Task獲取一份副本,轉化爲一個Executor獲取一份副本,直接減少了內存開銷,同時也避免了數據傾斜
def broadcastVal(sc: SparkContext): Unit = {
val kv = Map(("a",1),("b",2))
val bc = sc.broadcast(kv)
val rdd = sc.textFile("D:/io/input/txt/rdd.txt").flatMap(_.split("\\s+"))
rdd.foreach{
e=>{
if (bc.value.contains(e)) {
println(e)
}
}
}
}
通常來說只會將數據進行廣播,RDD並不能廣播,如果想將RDD廣播出去的話,可以將RDD拉回到Driver然後將RDD的數據在廣播出去
1.4.Kryo序列化
從Spark 2.0.0版本開始,簡單類型、簡單類型數組、字符串類型的Shuffling RDDs 已經默認使用Kryo序列化方式了。或者手動指定conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer");
1.5.本地化等待時長
1.6.算子優化
1.6.1.mapPartitions
1.6.2.foreachPartition
1.6.3.filter&coalesce
1.6.4.repartition
1.6.5.reduceByKey
2.資源配置
2.1.最優資源配置
--num-executors | executor個數 |
--driver-memory | Driver內存 |
--executor-cores | ExecutorCPU數量 |
--executor-memory | Executor內存 |
2.2.shuffle優化
2.2.1.Map緩衝區
默認32KB,手動指定:conf.set("spark.shuffle.file.buffer", "64")
2.2.2.Reduce緩衝區
默認48M,手動指定:conf.set("spark.reducer.maxSizeInFlight", "96")
2.2.3.Reduce重試次數
默認3次,手動指定:conf.set("spark.shuffle.io.maxRetries", "6")
2.2.4.Reduce等待間隔
默認5s,手動指定:conf.set("spark.shuffle.io.retryWait", "60s")
2.2.5.SortShuffle
默認200,手動指定:conf.set("spark.shuffle.sort.bypassMergeThreshold", "400")
2.3.JVM優化
對於JVM調優,首先應該明確,major gc/minor gc,都會導致JVM的工作線程停止工作,即stop the world
Spark 1.6 之後引入的統一內存管理機制,與靜態內存管理的區別在於存儲內存和執行內存共享同一塊空間,可以動態佔用對方的空閒區域,統一內存管理的堆內內存結構
2.3.1.Cache內存佔比
靜態內存管理機制:默認0.6,手動指定:conf.set("spark.storage.memoryFraction", "0.4")
統一內存管理機制:無需手動調節(動態佔用機制)
2.3.2.Executor對外內存
默認300M,手動指定:--conf spark.yarn.executor.memoryOverhead=2048
2.3.3.連接時長
默認60s,手動指定:--conf spark.core.connection.ack.wait.timeout=300