網易某業務線的計算資源資料治理實踐

陶然陶然發表於2023-03-16

  本文從計算資源治理實踐出發,帶大家清楚認識計算資源治理到底該如何進行,並如何應用到其他專案中。

   01 前言

  由於資料治理層面可以分多個層面且內容繁多(包括模型合規、資料質量、資料安全、計算/儲存資源、資料價值等治理內容),因此需要單獨拆分為6個模組單獨去闡述其中內容。

  筆者作為數倉開發經常會收到大量叢集資源滿載、任務產出延時等訊息/郵件,甚至下游數分及其他同學也會詢問任務執行慢的情況,在這裡很多數倉同學遇到這類問題第一想到的都是加資源解決,但事實真不一定是缺少資源,而是需要最佳化當前問題任務。所以本期從團隊做計算資源治理視角出發,帶大家清楚認識計算資源治理到底該如何進行。

   02 問題出現

  在做計算治理之前(2022.12)我們團隊盤點了下當前計算資源存在的幾個問題:

  (1)30+高消耗任務:由於數倉前中期業務擴張,要覆蓋大量場景應用,存在大量問題程式碼執行時資料傾斜,在消耗大量叢集計算資源下,產出時間也久;

  (2)200w+的小檔案:當前任務存在未合併小檔案、任務Reduce數量過多、上游資料來源接入(尤其是API資料接入)會造成過多小檔案出現,小檔案過多會開啟更多資料讀取,執行會浪費大量的資源,嚴重影響效能;

  (3)任務排程安排不合理:多數任務集中在凌晨2-5點執行且該區間CPU滿載,導致該時間段資源消耗成了重災區,所有核心/非核心任務都在爭搶資源,部分核心任務不能按時產出一直在等待階段;

  (4)線上無效DQC(資料質量監控)&監控配置資源過小:存在部分歷史任務沒下線表及DQC場景,每日都在空跑無意義DQC浪費資源,同時DQC資源過少導致DQC需要執行過長時間;

  (5)重複開發任務/無用任務:早期協助下游做了較多煙囪資料模型,因為種種原因,部分任務不再被使用,煙囪模型分散加工導致資源複用率降低;

  (6)任務缺少調優引數&部分任務仍然使用MapReduce/Spark2計算引擎:任務缺少調優引數導致資源不能適配及動態調整,甚至線上仍有早期配置MapReduce/Spark2計算引擎導致執行效率較低。

   03 思考與行動

  3.1 治理前的思考:

  在治理之前我想到一個問題,切入點該從哪裡開始最合適?

  經過與團隊多次腦暴對當前治理優先順序/改動成本大小/難度做了一個排序,我們先選擇從簡單的引數調優&任務引擎切換開始->小檔案治理->DQC治理->高消耗任務治理->排程安排->下線無用模型及沉澱指標到其他資料資產,同時在初期我們完成各類後設資料接入搭建治理看板以及團隊治理產出統計資料模型,並透過網易數帆提供的資料治理平臺解決具體細節問題。  

(資料治理平臺截圖)

  3.2 治理行動:

  (1)大部分任務切換至Spark3計算引擎&補充任務調優引數

  補充Spark調優引數(引數內容詳見文末),任務統一使用Spark3引擎加速,並充分利用Spark3的AQE特性及Z-Order排序演算法特性。

  AQE解釋:Spark 社群在 DAG Scheduler 中,新增了一個 API 在支援提交單個 Map 階段,以及在執行時修改 shuffle 分割槽數等等,而這些就是 AQE,在 Spark 執行時,每當一個 Shuffle、Map 階段進行完畢,AQE 就會統計這個階段的資訊,並且基於規則進行動態調整並修正還未執行的任務邏輯計算與物理計劃(在條件執行的情況下),使得 Spark 程式在接下來的執行過程中得到最佳化。

  Z-Order解釋:Z-Order 是一種可以將多維資料壓縮到一維的技術,在時空索引以及影像方面使用較廣,比如我們常用order by a,b,c 會面臨索引覆蓋的問題,Z-Order by a,b,c 效果對每個欄位是對等的

  (2)小檔案治理

  在這裡我們使用內部資料治理平臺-資料治理360對存在小檔案較多表提供內容展示(本質採集HDFS對應路徑下檔案數的日誌去顯示)

  當前小檔案處理:

  對於分割槽較多使用Spark3進行動態分割槽重新整理,(Spark3具備小檔案自動合併功能,如未使用Spark3可配置Spark3/Hive小檔案合併引數重新整理,引數詳見文末),程式碼如下:

  對於分割槽較少或未分割槽的表採用重建表,補資料方法回刷。

  小檔案預防:

  使用Spark3引擎,自動合併小檔案

  減少Reduce的數量(可以使用引數進行控制)

  用Distribute By Rand控制分割槽中資料量

  新增合併小檔案引數

  將資料來源抽取後的表做一個任務(本質也是回刷分割槽合併小檔案任務)去處理小檔案保障從資料來源開始小檔案不向下游流去

  (3)DQC治理

  無效DQC下線:難點在於需要查詢所有DQC對應的線上任務,檢視該DQC任務是否與線上任務一一匹配,從而找到無效DQC任務下線,內容繁雜耗時較多。

  DQC資源:由於之前DQC配置資源為叢集預設引數,效率極低導致所有DQC執行時長均超過10min,從而使得整體任務鏈路執行時長過久,調整Driver記憶體為2048M,Executor個數為2,Executor記憶體為4096M

  (4)高消耗任務調優

  這裡存在2個難點:最佳化效果不可控、高消耗任務調整到何種程度算合適,針對這個這個難點我們取所有核心資料資產任務均值,保障單個任務消耗小於平均消耗,同時我們針對當前高消耗任務列舉出如下可最佳化的方式:

  關聯表過多,需拆分

  關聯時一對多,資料膨脹

  資源配置過多,執行時資源嚴重浪費,需要將配置調小(包括Driver記憶體、Executor個數、Executor記憶體)

  程式碼結尾新增Distribute By Rand(),用來控制Map輸出結果的分發

  查詢中列和行未裁剪、分割槽未限定、Where條件未限定

  SQL中Distinct切換為Group by(Distinct會被hive翻譯成一個全域性唯一Reduce任務來做去重操作,Group by則會被hive翻譯成分組聚合運算,會有多個Reduce任務並行處理,每個Reduce對收到的一部分資料組,進行每組聚合(去重))

  關聯後計算切換為子查詢計算好後再關聯

  使用Map Join(Map Join會把小表全部讀入記憶體中,在Map階段直接拿另外一個表的資料和記憶體中表資料做匹配,由於在Map是進行了Join操作,省去了Reduce執行的效率也會高很多)可用引數代替

  (5)任務排程合理最佳化

  對於排程最佳化一開始會無從下手,統計凌晨2-5點區間下大概600+任務難梳理,同時存在任務依賴,修改起來可能會對下游整體有大的影響,因此我們選擇循序漸進先梳理再改善。

  找到所有表的輸出輸入點即啟始ODS與末尾ADS

  劃分其中核心表/非核心表,及對應任務開始時間與結束時間

  按照梳理內容把非核心的任務穿插在當前叢集資源非高峰時期(2點前與5點後),同時把核心任務排程提前,保障CDM層任務及時產出

  對實踐後內容再度調優,達到資源最大利用率

  (6)煙囪任務下沉&無用任務下線

  煙囪表過多,需下沉指標到DWS中提升複用性,對於無用任務也需要及時下線(這裡需要拿到後設資料血緣最好到報表層級的資料血緣,防止任務下線後導致視覺化內容問題產生),減少開發資源消耗。

   04 治理效果

  (1)Hive與Spark2任務升級Spark3.1,總計升級任務137個,升級任務後總體任務執行效率提升43%,cpu資源消耗降低41%,記憶體資源消耗降低46%

  (2)治理小檔案數大於10000+以上的數倉表總計30+張,小檔案總數由216w下降至67w

  (3)下線無效DQC任務總計50+,修改DQC配置資源降低執行時長,由原來10min最佳化至3min內

  (4)完成線上20+個任務最佳化及10+個任務下線及10+表指標下沉,最佳化後節省任務耗時146分鐘,減少CPU損耗800w+,降低記憶體消耗2600w+(相當於節省了8個200+欄位1億資料量任務消耗)

  (5)排程重新分配後2-5點資源使用率由90+%降低至50+%,保障日用資源趨勢圖無大突刺波動

   05 小結

  計算資源治理核心在於降本增效,用有限資源去執行更多工,透過一系列治理操作也讓數倉同學積累技術經驗同時規範化自身開發標準,讓治理反推進組內技術進步。

  計算資源治理是一件長久之事,並不能因為資源緊張才去治理,而要將計算治理常態化,可透過周/月資源掃描內容及時推送給每個同學,併為之打分,讓每個任務都有源可循,有方法可最佳化。

  引數內容

  引數並不是設定越多工效能越好,根據資料量、消耗、執行時間進行調整達到合理效果。

  Hive:

  (1)set hive.auto.convert.join = true; (是否自動轉化成Map Join)

  (2)set hive.map.aggr=true; (用於控制負載均衡,頂層的聚合操作放在Map階段執行,從而減輕清洗階段資料傳輸和Reduce階段的執行時間,提升總體效能,該設定會消耗更多的記憶體)

  (3)set hive.groupby.skewindata=true; (用於控制負載均衡,當資料出現傾斜時,如果該變數設定為true,那麼Hive會自動進行負載均衡)

  (4)set hive.merge.mapfiles=true; (用於hive引擎合併小檔案使用)

  (5)set mapreduce.map.memory.mb=4096; (設定Map記憶體大小,解決Memory佔用過大/小)

  (6)set mapreduce.reduce.memory.mb=4096;(設定Reduce記憶體大小,解決Memory佔用過大/小)

  (7)set hive.exec.dynamic.partition.mode=nonstrict;(動態分割槽開啟)

  Spark:

  (1)set spark.sql.legacy.parquet.datetimeRebaseModeInRead=LEGACY;(用於spark3中欄位型別不匹配(例如datetime無法轉換成date),消除sql中時間歧義,將Spark .sql. LEGACY . timeparserpolicy設定為LEGACY來恢復Spark 3.0之前的狀態來轉化)

  (2)set spark.sql.adaptive.enabled=true;(是否開啟調整Partition功能,如果開啟,spark.sql.shuffle.partitions設定的Partition可能會被合併到一個Reducer裡執行。平臺預設開啟,同時強烈建議開啟。理由:更好利用單個Executor的效能,還能緩解小檔案問題)

  (3)set spark.sql.hive.convertInsertingPartitionedTable=false;(解決資料無法同步Impala問題,使用Spark3引擎必填)

  (4)set spark.sql.finalStage.adaptive.advisoryPartitionSizeInBytes=2048M;(Spark小檔案合併)

來自 “ 網易有數 ”, 原文作者:語興;原文連結:http://server.it168.com/a2023/0316/6794/000006794222.shtml,如有侵權,請聯絡管理員刪除。

相關文章