一.Spark 性能優化概述
首先筆者能力優先,使用Spark有一段時間,如下是筆者的工作經驗的總結。
Spark任務運行圖:
Spark的優化思路:
一般是從3個層面進行Spark程序的優化:
- 運行環境優化
- RDD算子優化
- 參數微調
二.運行環境優化
2.1 數據本地性
我們知道HDFS的數據文件存儲在不同的datanode,一般數據副本數量是3,因為Spark計算的數據量比較大,如果數據不在本節點,需要通過網絡去其它的datanode讀取數據。
所以此時我們可以通過提高數據本地性,減少網絡傳輸,來達到性能優化的目的。
- 計算和存儲同節點(executor和HDFS的datanode、hbase的region server同節點)
- executor數目合適: 如果100個數據界定,3個計算節點,就有97份網絡傳遞,所以此種情況可以適當增加計算節點。
- 適當增加數據副本數量
2.2 數據存儲格式
推薦使用列式存儲格式: parquet.
parquet存在如下優先:
- 相同數據類型的數據有很高壓縮比
- Hive主要支持ORC、也支持parquet
三.RDD算子優化
3.1 盡可能復用同一個RDD
每創建一個RDD都會帶來性能的開銷,盡可能的對同一個RDD做算子操作,而不要頻繁創建新的
RDD。
3.2 對多次使用的RDD進行持久化
如果RDD的算子特別多,需要頻繁多次操作同一個RDD,最好的辦法是將該RDD進行持久化,
四.參數微調
num-executors
參數說明:該參數用于設置每個Executor進程的內存。Executor內存的大小,很多時候直接決定了Spark作業的性能,而且跟常見的JVM OOM異常,也有直接的關聯。executor-cores
參數說明:該參數用于設置每個Executor進程的CPU core數量。driver-memory
參數說明:該參數用于設置Driver進程的內存。spark.default.parallelism
參數說明:該參數用于設置每個stage的默認task數量。spark.storage.memoryFraction
參數說明:該參數用于設置RDD持久化數據在Executor內存中能占的比例,默認是0.6。spark.shuffle.memoryFraction
參數說明:該參數用于設置shuffle過程中一個task拉取到上個stage的task的輸出后,進行聚合操作時能夠使用的Executor內存的比例,默認是0.2。
資源參數參考示例:
./bin/spark-submit \
--master yarn-cluster \
--num-executors 100 \
--executor-memory 6G \
--executor-cores 4 \
--driver-memory 1G \
--conf spark.default.parallelism=1000 \
--conf spark.storage.memoryFraction=0.5 \
--conf spark.shuffle.memoryFraction=0.3 \
五.數據傾斜
絕大多數task執行得都非常快,但個別task執行極慢。比如,總共有1000個task,997個task都在1分鐘之內執行完了,但是剩余兩三個task卻要一兩個小時。這種情況很常見。
數據傾斜圖例:
解決數據傾斜一般有如下幾種常用方法:
使用Hive ETL預處理數據
先使用Hive進行預處理數據,也就是使用Hive先計算一層中間數據,Spark從中間層數據開始計算。過濾少數導致傾斜的key
如果發生導致傾斜的key非常少,可以將Spark任務拆分為包含 導致傾斜的key的任務和不包含key的任務。sample采樣傾斜key單獨進行join
通過采樣,提前預估會發生數據傾斜的key,然后將一個join拆分為兩個join,其中一個不包含該key,一個只包含該key,最后將結果集進行union。調整并行度
調整Shuffle并行度,數據打散廣播小數據集
適用于一個大表,一個小表
不用join連接操作,而改用Broadcast變量與map模擬join操作,完全規避shuffle操作
spark.sql: spark.sql.autoBroadcastJoinThreshold=104857600增加隨機前綴
對發生傾斜的RDD增加隨機前綴
對另外一個RDD等量擴容
如果少量的key發生傾斜,可以先過濾出一個單獨的RDD,對另外一個RDD同理吹,join之后再合并
六. Spark常用的調優參數
6.1 在內存中緩存數據
Spark SQL可以通過調用Spark.catalog.cachetable ("tableName")或DataFrame.cache()來使用內存中的columnar格式緩存表。然后Spark SQL將只掃描所需的列,并自動調優壓縮以最小化內存使用和GC壓力。你可以調用spark.catalog.uncacheTable("tableName")從內存中刪除表。
內存緩存的配置可以在SparkSession上使用setConf方法或者使用SQL運行SET key=value命令來完成。
參數名 | 默認值 | 參數說明 | 啟始版本 |
---|---|---|---|
spark.sql.inMemoryColumnarStorage.compressed | true | 當設置為true時,Spark SQL會根據數據統計自動為每列選擇壓縮編解碼器。 | 1.0.1 |
spark.sql.inMemoryColumnarStorage.batchSize | 10000 | 控制柱狀緩存的批大小。更大的批處理大小可以提高內存利用率和壓縮,但在緩存數據時可能會帶來OOMs風險。 | 1.1.1 |
6.2 其它配置項
還可以使用以下選項調優查詢執行的性能。隨著更多的優化被自動執行,這些選項可能會在未來的版本中被棄用。
參數名 | 默認值 | 參數說明 | 啟始版本 |
---|---|---|---|
spark.sql.files.maxPartitionBytes | 134217728 (128 MB) | 讀取文件時裝入單個分區的最大字節數。此配置僅在使用基于文件的源(如Parquet、JSON和ORC)時有效。 | 2.0.0 |
spark.sql.files.openCostInBytes | 4194304 (4 MB) | 打開一個文件的估計成本,由可以在同一時間掃描的字節數來衡量。當將多個文件放入一個分區時使用。最好是高估,那么帶有小文件的分區將比帶有大文件的分區更快(這是首先安排的)。此配置僅在使用基于文件的源(如Parquet、JSON和ORC)時有效。 | 2.0.0 |
spark.sql.files.minPartitionNum | Default Parallelism | 建議的(不是保證的)最小分割文件分區數。如果沒有設置,默認值是' spark.default.parallelism '。此配置僅在使用基于文件的源(如Parquet、JSON和ORC)時有效。 | 3.1.0 |
spark.sql.broadcastTimeout | 300 | broadcast join 等待時間的超時(秒) | 1.3.0 |
spark.sql.autoBroadcastJoinThreshold | 10485760 (10 MB) | 配置在執行聯接時將廣播到所有工作節點的表的最大字節大小。通過將此值設置為-1,可以禁用廣播。注意:目前統計只支持運行ANALYZE TABLE COMPUTE statistics noscan命令的Hive Metastore表。 | 1.1.0 |
spark.sql.shuffle.partitions | 200 | 配置將數據變換為連接或聚合時要使用的分區數量。 | 1.1.0 |
spark.sql.sources.parallelPartitionDiscovery.threshold | 32 | 配置閾值以啟用作業輸入路徑的并行列出。如果輸入路徑數大于該閾值,Spark將通過Spark分布式作業列出文件。否則,它將退回到順序列表。此配置僅在使用基于文件的數據源(如Parquet、ORC和JSON)時有效。 | 1.5.0 |
spark.sql.sources.parallelPartitionDiscovery.parallelism | 10000 | 配置作業輸入路徑的最大列出并行度。如果輸入路徑的數量大于這個值,它將被降低到使用這個值。與上面一樣,此配置僅在使用基于文件的數據源(如Parquet、ORC和JSON)時有效。 | 2.1.1 |
6.3 SQL查詢連接的hint
join策略提示BROADCAST、MERGE、SHUFFLE_HASH和SHUFFLE_REPLICATE_NL,在將指定的關系加入到另一個關系時,指示Spark對每個指定的關系使用暗示策略。例如,在表' t1 '上使用BROADCAST提示時,廣播加入(廣播散列連接或廣播嵌套循環聯接取決于是否有等值連接鍵)與t1的構建方面將由火花即使大小的優先表t1的建議的統計配置spark.sql.autoBroadcastJoinThreshold之上。
當連接兩端指定了不同的連接策略提示時,Spark會優先考慮BROADCAST提示而不是MERGE提示,優先考慮SHUFFLE_HASH提示而不是SHUFFLE_REPLICATE_NL提示。當雙方都指定了BROADCAST提示或SHUFFLE_HASH提示時,Spark將根據連接類型和關系的大小選擇構建端。
請注意,不能保證Spark會選擇提示中指定的連接策略,因為特定的策略可能不支持所有的連接類型。
-- We accept BROADCAST, BROADCASTJOIN and MAPJOIN for broadcast hint
SELECT /*+ BROADCAST(r) */ * FROM records r JOIN src s ON r.key = s.key
Coalesce hint允許Spark SQL用戶控制輸出文件的數量,就像Dataset API中的Coalesce、repartition和repartitionByRange一樣,它們可以用于性能調優和減少輸出文件的數量。COALESCE hint只有一個分區號作為參數?!癛EPARTITION”提示有一個分區號、列或它們都作為參數?!癛EPARTITION_BY_RANGE”提示必須有列名,分區號是可選的。
SELECT /*+ COALESCE(3) */ * FROM t
SELECT /*+ REPARTITION(3) */ * FROM t
SELECT /*+ REPARTITION(c) */ * FROM t
SELECT /*+ REPARTITION(3, c) */ * FROM t
SELECT /*+ REPARTITION_BY_RANGE(c) */ * FROM t
SELECT /*+ REPARTITION_BY_RANGE(3, c) */ * FROM t
6.4 自適應查詢執行
Adaptive Query Execution (AQE)是Spark SQL中的一種優化技術,它利用運行時統計信息來選擇最高效的查詢執行計劃。默認情況下AQE是禁用的。Spark SQL可以使用Spark.SQL.adaptive.enabled的傘配置來控制是否打開/關閉。從Spark 3.0開始,AQE中有三個主要特性,包括合并shuffle后分區、將排序合并連接轉換為廣播連接以及傾斜連接優化。
6.5 合并分區后重新組合
當spark.sql.adaptive.enabled和spark.sql.adaptive.coalescePartitions.enabled配置都為true時,該特性根據map輸出統計信息來合并post shuffle分區。這個特性簡化了運行查詢時shuffle分區號的調優。您不需要設置合適的shuffle分區號來適合您的數據集。一旦您通過Spark .sql. adaptive.coalescepartitions . initialpartitionnum配置設置了足夠大的初始shuffle分區數,Spark就可以在運行時選擇適當的shuffle分區號。
參數名 | 默認值 | 參數說明 | 啟始版本 |
---|---|---|---|
spark.sql.adaptive.coalescePartitions.enabled | true | 當true和Spark .sql. adaptive_enabled為true時,Spark會根據目標大小(由Spark .sql. adaptive_advisorypartitionsizeinbytes指定)合并連續的shuffle分區,以避免過多的小任務。 | 3.0.0 |
spark.sql.adaptive.coalescePartitions.minPartitionNum | Default Parallelism | 合并后的最小洗牌分區數。如果不設置,則默認為Spark集群的默認并行度。此配置僅在spark.sql. adaptive.net enabled和spark.sql. adaptive.net coalescepartitions .enabled同時啟用時有效。 | 3.0.0 |
spark.sql.adaptive.coalescePartitions.initialPartitionNum | 200 | 合并前的初始shuffle分區數。默認情況下它等于spark.sql.shuffle.partitions。此配置僅在spark.sql. adaptive.net enabled和spark.sql. adaptive.net coalescepartitions .enabled同時啟用時有效。 | 3.0.0 |
spark.sql.adaptive.advisoryPartitionSizeInBytes | 64 MB | 自適應優化期間shuffle分區的建議大小(當spark.sql. adaptive_enabled為true時)。當Spark對小shuffle分區或斜shuffle分區進行合并時生效。 | 3.0.0 |
6.6 將排序合并聯接轉換為廣播聯接
當任何連接側的運行時統計數據小于廣播散列連接閾值時,AQE將排序合并連接轉換為廣播散列連接。這不是一樣有效規劃一個廣播散列連接首先,但這總比繼續做分類合并加入,我們可以節省連接雙方的排序,并在本地讀取洗牌文件節省網絡流量(如果spark.sql.adaptive.localShuffleReader.enabled被設置為true)
6.7 優化傾斜連接
數據傾斜會嚴重降低連接查詢的性能。該特性通過將傾斜任務拆分(如果需要的話還可以復制)為大小大致相同的任務,動態處理排序-合并連接中的傾斜任務。當spark.sql.adaptive.enabled和spark.sql.adaptive.skewJoin.enabled配置同時啟用時生效。
參數名 | 默認值 | 參數說明 | 啟始版本 |
---|---|---|---|
spark.sql.adaptive.skewJoin.enabled | true | 當true和Spark .sql.adaptive.enabled為true時,Spark通過拆分(并在需要時復制)傾斜分區來動態處理排序-合并連接中的傾斜。 | 3.0.0 |
spark.sql.adaptive.skewJoin.skewedPartitionFactor | 10 | 如果一個分區的大小大于這個因子乘以中值分區大小,并且大于spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes,則認為該分區是傾斜的。 | 3.0.0 |
spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes | 256MB | 如果分區的字節大小大于這個閾值,并且大于spark.sql.adaptive.skewJoin.skewedPartitionFactor乘以分區中值大小,則認為該分區是傾斜的。理想情況下,該配置應該設置為大于spark.sql.adaptive.advisoryPartitionSizeInBytes。 | 3.0.0 |