Hudi 方案 | 網際網路場景下基於拉鍊表的全量表極限儲存優化方案

語言: CN / TW / HK

本期作者

董子平

嗶哩嗶哩資深開發工程師

負責B站主站數倉團隊社群、資料安全運營,專注於資料倉庫、資料安全的落地、應用和推廣。

01 背景

2020年以來,半導體生產不足,這個問題困擾著全世界。網際網路企業高度依賴於網路基礎設施和伺服器設施,沒有半導體就沒有滿足個人需要的個人電子消費品,也就沒有蓬勃發展的網際網路行業。

嗶哩嗶哩作為一家以多媒體內容為核心服務的公司,亦高度依賴於企業級伺服器裝置,需要大量的伺服器裝置進行資料計算和資料儲存。晶片荒引起採購週期的延長、採購數量的被動減少,甚至導致裝置一機難求。隨著大量人群瞭解B站、湧入B站、喜歡B站,在B站產生了大量的資料,嗶哩嗶哩對伺服器數量的需求越來越高,一增一減之間,資料建設陷入了計算不夠用、儲存不夠用的窘境。

02 資料分析

面臨這種資源緊缺的困難,資料平臺部對主站數倉的資料進行了多維度、全場景的統計分析。經過分類,得出以下四類資料:

  1. 日誌型資料

  2. 業務型資料

  3. 手工產出資料

  4. 元資料

手工產出資料和元資料對於大資料體系來說,資料量一般很小,對計算資源的需求也不多。資料佔比更多的是日誌型資料和業務型資料。

對於日誌型資料,又分為客戶端日誌和服務端日誌,但無論哪類日誌,一般來說均屬於增量類、靜態類資料,一旦產生即不會對資料本身進行再次變更。

對於業務型資料,主要來源於業務資料庫中的生產資料,即傳統的會進行DDL、DML等操作的資料,會頻繁的進行增刪改查。在資料倉庫使用時,需要計算出最新狀態,供下游使用。

03 全量分割槽表模式

經過對以上幾類典型資料的調研和分析,並和各個網際網路企業進行溝通和諮詢,我們發現整個網際網路行業,大量企業採用Ralph Kimall大師的維度建模方法論為指導。對於業務型資料,為了便於ETL使用、便於分析使用,基本上都做成全量分割槽表的模式。

對於剛剛開始建設資料倉庫的團隊,一般會永久儲存,如果受限於儲存容量,會寫crontab任務定期刪除最早的歷史分割槽。對於資料倉庫建設成熟的團隊,一般會有專門的資料治理團隊負責資料的治理,對接資料開發同學確定資料的生命週期,並對全量分割槽表制定自動化策略,命中策略後自動刪除資料。

全量分割槽表:

表按照T-1進行建立分割槽,分割槽值一般為資料生產時的昨天,如資料在2022-02-26 01:02:03.456生產完成,則分割槽值為2022-02-25;每個分割槽儲存自資料生產當日00:00:00.000以前的全量資料,即2022-02-26 00:00:00.000以前的全量資料。

目前,嗶哩嗶哩資料平臺部已步入成熟的資料建設階段,也會有專門的資料治理團隊進行治理,把全量分割槽表的生命週期配置為永久或固定時間週期,在資源充足的背景下,資料倉庫開發生產資料、下游任務消費資料均簡單和成熟,業內也都是這麼做的。

但是當資源緊張時,這種模式的缺點會被快速放大:

  1. 資料儲存浪費嚴重。

    對於沒有變化的資料,資料在全量分割槽表的每一份分割槽中都儲存一份,會存在重複儲存問題,當資料變化率低時重複儲存比例大大增大。

  2. 資料存在丟失的問題

    這與資料倉庫的基本理念不符。如果定期刪除全量分割槽表的最早歷史分割槽,在進行資料分析時,無法滿足“隨時間變化的”、“ 資訊本身相對穩定的”。

資料倉庫是一個面向主題的、整合的、隨時間變化的、資訊本身相對穩定的資料集合,用於對管理決策過程的支援。

針對以上場景,從技術上考量,第一種方案是上文所述的全量分割槽表,按照離線模式T-1同步增量資料到資料倉庫,然後T-2的全量資料並上T-1的增量資料,生產出最新的T-1全量資料,整個鏈路均為離線鏈路。 具有時效低、效能高、吞吐大的特點。

SQL虛擬碼如下:

insert overwrite table 目 標 表 partition (分 區 = 昨 天)

select
字 段 列 表
from(
select
字 段 列 表,
row_number() over(
partition by 主 鍵 列 表
order by
更 新 字 段 desc
) as rn
from
(
select
字 段 列 表
from
增 量 表
where
分 區 = 昨 天
union all
select
字 段 列 表
from
全 量 表
where
分 區 = 前 天
) n
) t
where
rn = 1

第二種方案是現階段很熱的資料湖,比如Hudi,結合Flink CDC,基於Mysql的binlog實時記錄收集資料新增、更新等資訊,實時更新資料到最新狀態。

在初始化時,以離線模式批量從資料庫中拉取全量資料,初始化到Hudi表中;訂閱資料庫的增量資料,增量更新到Hudi表中。資料以分鐘級的延遲和資料庫保持完全一致。

具有時效高、效能中、吞吐中的特點。經過基準效能測試,日新增、變更低於1000萬條的資料,使用Hudi+Flink CDC可以較好的實現資料的合併,生產出資料的準實時資料。對於資料的歷史所有變更的全版本儲存功能現有社群功能還需要繼續完善。

從業務上考量,第三種方案是資料倉庫模型設計上拉鍊表的概念,通過記錄歷史所有資料的狀態和資料的生命週期,保留所有的資料快照。 從語義上和技術實現上來看,這和第一種方案的全量分割槽表可以保證完全一致,資料可以做全等校驗和檢測,下游使用者遷移時,可以無縫進行遷移,縮短全量分割槽表的生命週期時,下游使用者無感知。

04 基於拉鍊表的全量表優化方案

基於以上幾種方案,資料團隊和技術團隊經過多次的溝通和討論,推進第二種方案Hudi + Flink CDC和第三種方案拉鍊表支援研發。

Hudi+Flink CDC用於支援新型準實時需求類需求,對於時效性要求高的需求,比如需要分鐘級的延遲,以Hudi+Flink CDC進行支援。

拉鍊表方案做存量全量分割槽表的無縫遷移,和支援離線T-1類的時效性要求較低的需求,以及需要歷史所有變更的全版本下的支援。

拉鍊表:針對資料倉庫設計中表儲存資料的方式而定義的一種儲存規範,顧名思義,所謂拉鍊,就是記錄歷史。記錄一個事物從開始,一直到當前狀態的所有變化的資訊。

假設我們有一份使用者表,裡面有兩個欄位,第一個是使用者ID,每個使用者唯一且不變,第二個欄位是使用者暱稱,使用者可以自己任意更新自己的暱稱。

經過對資料重複率和資料覆蓋率測試,對比一年前的使用者資料和最新的使用者資料,重複率為9990‱(萬分之)。即一年前的資料經過一年後,僅有10‱進行了變更。對於這種重複率的資料,我們可以對全量分割槽表拉鍊表化。

基於本專案對一份資料進行了拉鍊化測試,之前資料量1.20PB,拉鍊化後降低到5.06TB,優化率99.578%;每日產出任務的IO資源消耗由6.11TB+7.60GB降低到5.06TB+7.60GB,IO和計算資源的優化率均為17.16%。

拉鍊表生產的資料樣例如下:

生產此拉鍊表的SQL為:

INSERT OVERWRITE TABLE 拉鍊表
SELECT
n1.id,
n1.暱稱,
n1.start_date,
CASE
WHEN n1.end_date = '9999-12-31'
AND n2.id IS NOT NULL THEN '業務日期-1'
ELSE n1.end_date
END AS end_date
FROM 拉鍊表 n1
LEFT OUTER JOIN
(SELECT id FROM 使用者表
WHERE 昨日新註冊 OR 昨日變更暱稱) n2 ON n1.id = n2.id
UNION ALL
SELECT id, 暱稱, '業務日期' as start_date, '9999-12-31' as end_date
FROM 使用者表
WHERE
昨日新註冊 OR 昨日變更暱稱

關於這個sql:

經過上述加工,可以生產出一份反應歷史資料變化的拉鍊表資料。雖說大大降低了儲存,但是有沒有方案不增加增量資料的讀取次數,且不增加任務數?通過對拉鍊表的生產邏輯進行了深入分析,僅僅靠SQL是無法達到目的的,需要開發一個通用型的技術方案,下面講述一下我們的思路,由於大資料系統最早以MapReduce發展起來的,本文所述的技術方案以MapReduce為基本框架,歡迎大家一起交流。

整個MapReduce的資料流如下:

上述MR生產拉鍊表資料時,相對於傳統的拉鍊表生產SQL,僅需要一個任務、讀取一次拉鍊表、讀取一次增量資料,且不需要單獨增加一步排序的步驟。在節省儲存資源的同時,又能節省計算資源。

05 總結

對全量分割槽表進行拉鍊化,優化的技術方案不僅能降低儲存,而且還能降低一定的計算資源。

後續我們會和大家聊聊如何進行拉鍊表的冪等化,如何高效地對資料進行覆蓋率和重複率測試,預估全量分割槽表拉鍊化的收益以及有選擇性地優化全量分割槽表等內容。