Dataworks批量刷數優化方案探討

福祿網路研發團隊發表於2021-11-04

Dataworks批量刷數優化方案探討

在資料倉儲的日常使用中,經常會有批量補資料,或者邏輯調整後批量重跑資料的場景。
批量刷數的實現方式,因排程工具差異而各有不同。

Dataworks排程批量刷數侷限

我們的資料倉儲構建在阿里雲的dataworks+maxcompute產品上,dataworks的排程工具提供了補資料的功能,可以很方便的補整個任務流的資料,但是該功能有個侷限,就是隻能指定一個引數,即業務日期,如下圖。
20211102095700
如果要刷一個月的資料,比如2021年10月份,要怎麼操作呢?業務日期選定時間範圍2021-10-01 ~ 2021-10-31。然後dataworks會根據選定的時間範圍,每天生成一個例項去執行任務補資料,也就是補資料的任務要跑31次,每次補一天的資料。
20211102095723
這樣就會導致整個補數的過程非常緩慢,且耗資源。

  1. 因為maxcompute是基於hive的,一個任務的啟動初始化-〉申請資源-〉等待資源分配的過程是很重、很緩慢的,31個天任務的這個過程中耗時會是單個任務的31倍(未並行的情況下)。初步統計了一下,單個maxcompute任務的啟動耗時大概是8s,31個任務啟動就比單個任務多出了4分鐘,如果整個流程涉及10個任務,整個刷數的耗時單在這個階段就要多出40分鐘。
  2. 作為大資料的計算引擎,在一定的資料量級內,資料處理的耗時差別並不大。也就是說一次處理一天的資料,相較於一次處理一個月的資料,正常而言區別不會很大,絕對低於處理30次一天資料的耗時。

所以想要有效率的補數,不能依靠這種方式。

優化方案1

提升批量刷數效率的第一步,就是改造任務,按時間範圍跑數,比如一次跑一個月的資料。但是這與日常排程的任務又有衝突,所以需要新建一份做為手動任務釋出執行。
同時dataworks的手動任務的執行,也只能指定一個業務日期引數,且沒法選定業務日期範圍,即批量生成例項。
20211102095741
介於這個限制,所以手動任務的跑數時間週期只能是固定的,比如月、周、年。
按照這種方式刷數的效率,也已經遠遠高於前面的多例項按天補資料的方式。
實際案例,一個涉及近30個任務的流程,通過補資料的多例項按天刷一個月的數,耗時需6.5~8小時。通過手動任務按月刷數耗時只需1.5~2小時,效率提升近75%。

這種方式的缺點是:

  1. 同一指令碼要維護兩套,一套週期任務,一套手動任務。增加維護成本。
  2. 指令碼跑數時間週期固定,如果需要調整週期需改指令碼,並單獨釋出。比如按月刷數的指令碼,想要按年刷,需要改指令碼重新發布。

優化方案2

仔細分析,造成批量刷數麻煩、效率低下的根本原因,其實是dataworks的排程工具(不管是補資料,還是手工任務執行)只能指定一個引數
有其他排程工具使用經驗的朋友都會知道,在用排程工具手動執行任務時,一般是可以指定任務的入參的。而一般的ETL指令碼都會設定兩個入參: 開始時間、結束時間,來支援按時間範圍跑數。
而阿里雲dataworks排程工具支援自定義傳參的唯一方式是賦值節點。通過賦值節點的輸出引數,將引數傳遞給下游任務,下游任務可通過設定依賴賦值節點,獲取賦值節點的輸出引數,作為本節點任務的入參。
具體配置方法,可參考dataworks的說明文件:https://help.aliyun.com/document_detail/137534.html

所以具體的優化思路是,通過控制賦值節點的輸出值,來指定下游任務的跑數時間範圍,以實現批量刷數的目的。

賦值節點配置

因賦值節點的輸出值需要可人為調整,所以設計建立一參數列,賦值節點從表裡獲取資料,作為輸出值。可通過update參數列的值,來調整賦值節點輸出值。

因為要update表,所以需建立一個支援事務(update)的表,同時需要包含下游節點需要的兩個引數:開始時間、結束時間。

如:

CREATE TABLE IF NOT EXISTS schedule_args(
  start_date BIGINT COMMENT '開始日期,格式:yyyymmdd'
  ,end_date BIGINT COMMENT '結束日期,格式:yyyymmdd'
) STORED AS ALIORC 
TBLPROPERTIES ('comment'='排程任務入參表', 'transactional'='true')
;

又,因為只有週期任務支援賦值節點,且下游任務都得從該節點獲取跑數時間範圍。所以這裡會有個風險點: 對賦值節點引數的調整可能影響到下游任務的日常排程。

解決方法是:通過增加判斷邏輯,把日常排程與補資料兩種場景的引數生成區別開。
具體實現是:通過獲取dataworks內建業務日期引數$bizdate,並對業務日期值的判斷,來區分週期排程和補資料兩種場景。 正常週期排程的業務日期都是前一天,而補資料指定的業務日期不是。

所以賦值節點的指令碼如下:

select case when '${bizdate}' <> to_char(dateadd(getdate(),-1,'dd'),'yyyymmdd') then start_date else '${bizdate}' end as start_date
,case when '${bizdate}' <> to_char(dateadd(getdate(),-1,'dd'),'yyyymmdd') then end_date else '${bizdate}' end as end_date
from schedule_args ;

注: 更完備的規避風險的方法是,通過python指令碼從表獲取引數,並增加對空表和表不存在兩種異常場景的處理。

這樣正常日排程,賦值節點生成的輸出引數值都是前一天,與不使用賦值節點獲取的dataworks業務日期引數一致。

簡單測試

配置下游節點,測試賦值節點的引數生成和下游節點的引數獲取,以及日常排程和補資料場景的切換。

下游測試節點配置:
20211102095834
20211102095844

節點流程如下:
20211102095857

參數列配置:
20211102095911

業務日期選擇前一天,模擬日常排程場景:
20211102095932

下游節點獲取入參為正常日排程的業務日期:
20211102095950

補資料場景:
20211102100014

下游節點獲取入參為參數列配置的日期:
20211102100215

小結

對於刷數時間週期固定,業務邏輯穩定變更少的任務流程,方案1比較適合。
方案2解決了方案1同一任務需要同時維護多套的問題,同時能夠動態調整跑數的時間週期,支援批量刷數的場景更多,也更方便。唯一存在的問題是,在多人同時通過調整參數列進行批量刷數的時候,會有衝突。
比如:開發者A,update了參數列的值為20211001、20211029。在他執行補資料例項之前,開發者B也要刷資料,將參數列的值改為了20201001、20201030。開發者A再去執行補資料時,獲取的引數時開發者B修改後的引數值20201001、20201030。
因為maxcompute還沒有鎖表機制,所以這個問題目前還沒有很好的解決方案,只能通過收攏表修改許可權等方式,來人為規避這個問題。

福祿·研發中心 福星

相關文章