個推技術 | 性能提升60%↑ 成本降低50%↓ Spark性能調優看這篇就夠了!

前言

Spark是目前主流的大數據計算引擎,功能涵蓋了大數據領域的離線批處理、SQL類處理、流式/實時計算、機器學習、圖計算等各種不同類型的計算操作,應用範圍與前景非常廣泛。作爲一種內存計算框架,Spark運算速度快,並能夠滿足UDF、大小表Join、多路輸出等多樣化的數據計算和處理需求。

作爲國內專業的數據智能服務商,個推從早期的1.3版本便引入Spark,並基於Spark建設數倉,進行大規模數據的離線和實時計算。由於Spark 在2.x版本之前的優化重心在計算引擎方面,而在元數據管理方面並未做重大改進和升級。因此個推仍然使用Hive進行元數據管理,採用Hive元數據管理+ Spark計算引擎的大數據架構,以支撐自身大數據業務發展。個推還將Spark廣泛應用到報表分析、機器學習等場景中,爲行業客戶和政府部門提供實時人口洞察、羣體畫像構建等服務。

▲個推在實際業務場景中,分別使用SparkSQL 和 HiveSQL對一份3T數據進行了計算,上圖展示了跑數速度。數據顯示:在鎖死隊列(120G內存,<50core)前提下, SparkSQL2.3 的計算速度是Hive1.2 的5-10倍。

對企業來講,效率和成本始終是其進行海量數據處理和計算時所必須關注的問題。如何充分發揮Spark的優勢,在進行大數據作業時真正實現降本增效呢?個推將多年積累的Spark性能調優妙招進行了總結,與大家分享。

Spark性能調優-基礎篇

衆所周知,正確的參數配置對提升Spark的使用效率具有極大助力。因此,針對 不瞭解底層原理的Spark使用者,我們提供了可以直接抄作業的參數配置模板,幫助相關數據開發、分析人員更高效地使用Spark進行離線批處理和SQL報表分析等作業。

推薦參數配置模板如下:

  1. Spark-submit 提交方式腳本

 

  1. spark-sql 提交方式腳本

 

Spark性能調優-進階篇

針對有意願瞭解Spark底層原理的讀者,本文梳理了standalone、Yarn-client、Yarn-cluster等3種常見任務提交方式的交互圖,以幫助相關使用者更直觀地理解Spark的核心技術原理、爲閱讀接下來的進階篇內容打好基礎。

standalone

1)   spark-submit 提交,通過反射的方式構造出1個DriverActor 進程;

2)   Driver進程執行編寫的application,構造sparkConf,構造sparkContext;

3)   SparkContext在初始化時,構造DAGScheduler、TaskScheduler,jetty啓動webui;

4)   TaskScheduler 有sparkdeployschedulebackend 進程,去和Master通信,請求註冊Application;

5)   Master 接受通信後,註冊Application,使用資源調度算法,通知Worker,讓worker啓動Executor;

6)   worker會爲該application 啓動executor,executor 啓動後,會反向註冊到TaskScheduler;

7)   所有Executor 反向註冊到TaskScheduler 後,Driver 結束sparkContext 的初始化;

8)   Driver繼續往下執行編寫的application,每執行到1個action,就會創建1個job;

9)   job 會被提交給DAGScheduler,DAGScheduler 會對job 劃分爲多個stage(stage劃分算法),每個stage創建1個taskSet;

10)  taskScheduler會把taskSet裏每1個task 都提交到executor 上執行(task 分配算法);

11)  Executor 每接受到1個task,都會用taskRunner來封裝task,之後從executor 的線程池中取出1個線程,來執行這個taskRunner。(task runner:把編寫的代碼/算子/函數拷貝,反序列化,然後執行task)。

 

 

Yarn-client

1)   發送請求到ResourceManager(RM),請求啓動ApplicationMaster(AM);

2)   RM 分配container 在某個NodeManager(NM)上,啓動AM,實際是個ExecutorLauncher;

3)   AM向RM 申請container;

4)   RM給AM 分配container;

5)   AM 請求NM 來啓動相應的Executor;

6)   executor 啓動後,反向註冊到Driver進程;

7)   後序劃分stage,提交taskset 和standalone 模式類似。

 

Yarn-cluster

1)   發送請求到ResourceManager(RM),請求啓動ApplicationMaster(AM);

2)   RM 分配container 在某個NodeManager(NM)上,啓動AM;

3)   AM向RM 申請container;

4)   RM給AM 分配container;

5)   AM 請求NM 來啓動相應的Executor;

6)   executor 啓動後,反向註冊到AM;

7)   後序劃分stage,提交taskset 和standalone 模式類似。

 

 

理解了以上3種常見任務的底層交互後,接下來本文從存儲格式、數據傾斜、參數配置等3個方面來展開,爲大家分享個推進行Spark性能調優的進階姿勢。

 

存儲格式(文件格式、壓縮算法)

 

衆所周知,不同的SQL引擎在不同的存儲格式上,其優化方式也不同,比如Hive更傾向於orc,Spark則更傾向於parquet。同時,在進行大數據作業時,點查、寬表查詢、大表join操作相對頻繁,這就要求文件格式最好採用列式存儲,並且可分割。因此我們推薦以parquet、orc 爲主的列式存儲文件格式和以gzip、snappy、zlib 爲主的壓縮算法。在組合方式上,我們建議使用parquet+gzip、orc+zlib的組合方式,這樣的組合方式兼顧了列式存儲與可分割的情況,相比txt+gz 這種行式存儲且不可分割的組合方式更能夠適應以上大數據場景的需求。

 

個推以線上500G左右的數據爲例,在不同的集羣環境與SQL引擎下,對不同的存儲文件格式和算法組合進行了性能測試。測試數據表明:相同資源條件下,parquet+gz 存儲格式較text+gz存儲格式在多值查詢、多表join上提速至少在60%以上。

 

結合測試結果,我們對不同的集羣環境與SQL引擎下所推薦使用的存儲格式進行了梳理,如下表:

 

同時,我們也對parquet+gz、orc+zlib的內存消耗進行了測試。以某表的單個歷史分區數據爲例,parquet+gz、orc+zlib比txt+gz 分別節省26%和49%的存儲空間。

 

完整測試結果如下表:

 

可見,parquet+gz、orc+zlib確實在降本提效方面效果顯著。那麼,如何使用這兩種存儲格式呢?步驟如下:

 

➤hive 與 spark 開啓指定文件格式的壓縮算法

  • spark: set spark.sql.parquet.compression.codec=gzip; set spark.sql.orc.compression.codec=zlib;
  • hive: set hive.exec.compress.output=true; set mapreduce.output.fileoutputformat.compress=true; set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.GzipCodec;

 

➤建表時指定文件格式

  • parquet 文件格式(序列化,輸入輸出類) CREATE EXTERNAL TABLE `test`(rand_num double) PARTITIONED BY (`day` int) ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat' ;
  • orc 文件格式(序列化,輸入輸出類) ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.orc.OrcSerde' STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.orc.OrcInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat' ;

 

➤線上表調

  •  
ALTER TABLE db1.table1_std SET TBLPROPERTIES ('parquet.compression'='gzip');
ALTER TABLE db2.table2_std SET TBLPROPERTIES ('orc.compression'='ZLIB');

 

➤ctas 建表

  •  
create table tablename stored as parquet as select ……;create table tablename stored as orc TBLPROPERTIES ('orc.compress'='ZLIB')  as select ……;

 

數據傾斜

 

數據傾斜分爲map傾斜和reduce傾斜兩種情況。本文着重介紹reduce 傾斜,如SQL 中常見的group by、join 等都可能是其重災區。數據傾斜發生時,一般表現爲:部分task 顯著慢於同批task,task 數據量顯著大於其他task,部分taskOOM、spark shuffle 文件丟失等。如下圖示例,在duration 列和shuffleReadSize/Records列,我們能明顯發現部分task 處理數據量顯著升高,耗時變長,造成了數據傾斜:

 

如何解決數據傾斜?

我們總結了7種數據傾斜解決方案,能夠幫助大家解決常見的數據傾斜問題:

 

解決方案一:使用 Hive ETL預處理數據

即在數據血緣關係中,把傾斜問題前移處理,從而使下游使用方無需再考慮數據傾斜問題。

該方案適用於下游交互性強的業務,如秒級/分鐘級別提數查詢。

 

解決方案二:過濾少數導致傾斜的key

即剔除傾斜的大key,該方案一般結合百分位點使用,如99.99%的id 記錄數爲100條以內,那麼100條以外的id 就可考慮予以剔除。

該方案在統計型場景下較爲實用,而在明細場景下,需要看過濾的大key 是否爲業務所側重和關注。

 

解決方案三:提高shuffle操作的並行度

即對spark.sql.shuffle.partitions參數進行動態調整,通過增加shuffle write task寫出的partition數量,來達到key的均勻分配。SparkSQL2.3 在默認情況下,該值爲200。開發人員可以在啓動腳本增加如下參數,對該值進行動態調整:

  • conf spark.sql.shuffle.partitions=10000
  • conf spark.sql.adaptive.enabled=true 
  • conf spark.sql.adaptive.shuffle.targetPostShuffleInputSize=134217728 

 

該方案非常簡單,但是對於key的均勻分配卻能起到較好的優化作用。比如,原本10個key,每個50條記錄,只有1個partition,那麼後續的task需要處理500條記錄。通過增加partition 數量,可以使每個task 都處理50條記錄,10個task 並行跑數,耗時只需要原來1個task 的1/10。但是該方案對於大key較難優化,比如,某個大key記錄數有百萬條,那麼大key 還是會被分配到1個task 中去。

 

解決方案四:將reducejoin轉爲mapjoin

指的是在map端join,不走shuffle過程。以Spark爲例,可以通過廣播變量的形式,將小RDD的數據下發到各個Worker節點上(Yarn 模式下是NM),在各個Worker節點上進行join。

該方案適用於小表join大表場景(百G以上的數據體量)。此處的小表默認閾值爲10M,低於此閾值的小表,可分發到worker節點。具體可調整的上限需要小於container分配的內存。

 

解決方案五:採樣傾斜key並分拆join操作

如下圖示例:A表 join B表,A表有大key、B表無大key,其中大key的id爲1,有3條記錄。

如何進行分拆join操作呢?

  • 首先將A表、B表中id1單獨拆分出來,剔除大key的A' 和 B' 先join,達到非傾斜的速度;

  • 針對A表大key添加隨機前綴,B表擴容N倍,單獨join;join後剔除隨機前綴即可;

  • 再對以上2部分union。

該方案的本質還是減少單個task 處理過多數據時所引發的數據傾斜風險,適用於大key較少的情況。

 

解決方案六:使用隨機前綴和擴容RDD進行join

比如,A 表 join B表,以A表有大key、B表無大key爲例:

  • 對A表每條記錄打上[1,n] 的隨機前綴,B表擴容N倍,join。

  • join完成後剔除隨機前綴。

該方案適用於大key較多的情況,但也會增加資源消耗。

 

解決方案七:combiner

即在map端做combiner操作,減少shuffle 拉取的數據量。

該方案適合累加求和等場景。

 

在實際場景中,建議相關開發人員具體情況具體分析,針對複雜問題也可將以上方法進行組合使用。

 

Spark 參數配置

 

針對無數據傾斜的情況,我們梳理總結了參數配置參照表幫助大家進行Spark性能調優,這些參數的設置適用於2T左右數據的洞察與應用,基本滿足大多數場景下的調優需求。

 

總結

目前,Spark已經發展到了Spark3.x,最新版本爲Spark 3.1.2 released (Jun 01, 2021)。Spark3.x的許多新特性,如動態分區修剪、Pandas API的重大改進、增強嵌套列的裁剪和下推等亮點功能,爲進一步實現降本增效提供了好思路。未來,個推也將繼續保持對Spark演進的關注,並持續展開實踐和分享。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章