58基於Flink構建實時數倉實踐

導讀

隨着公司用戶增長業務快速發展,陸續孵化出 部落、同鎮、C 端會員、遊戲等非常多的業務板塊。與此同時產品及運營對實時數據需求逐漸增多,幫助他們更快的做出決策,更好的進行產品迭代,實時數倉的建設變得越發重要起來。本文主要介紹用戶增長業務基於 Flink 構建實時數倉的實踐之路。

實時數倉1.0介紹

如下圖是早期的實時計算架構,實時數據需求較少,架構簡單,數據鏈路少,一路到底的開發模式能很快滿足業務需求;

       

但是,隨着產品和業務人員對實時數據需求的不斷增多,上面的架構帶來了如下的問題:

  1. 需求驅動的煙囪式開發,導致開發成本居高不下;

  2. 數據模型,數據分層缺失,導致實時計算資源成本高,數據指標一致性不可控;

  3. 數據使用場景變多,明細數據,多維分析,業務指標計算等,單一的需求驅動式開發模式難以應付多種需求場景;

基於以上的問題,我們開始構建 2.0 版本的實時計算架構。

實時數倉2.0介紹

一、模型

我們參考離線數倉,將實時數倉按照分層設計的思想分爲三層,如上圖所示,分別爲:

ODS 層:存儲 APP 各業務線埋點數據以及業務端各種日誌;

DW 之明細層:分爲公共層和業務層,APP 客戶端收集的埋點數據通過關聯維表,統一將產品,渠道,版本,類別,地域等客戶端公共維度屬性進行 ETL,並統一進行去重,過濾,分流等動作,這樣就生成了公共行爲明細表和業務行爲明細表;然後業務明細表關聯各自業務維表形成了業務主題明細表,比如同鎮業務,部落業務等;

DW 之彙總層:讀取業務主題明細表計算出各業務主題關心的通用維度和指標,並存儲到 自研的wtable中;(wtable 是 公司自研的分佈式 kv、klist 存儲系統 )

ADS 層:這一層主要提供 ad-hoc 查詢和實時大盤服務,其中 ad-hoc 查詢是指通過 Flink 將主題明細寬表實時導入到 ClickHouse 中,爲分析師和產品提供 ad-hoc 查詢;而實時大盤主要是讀取 wtable 中存儲的DWS多維彙總指標來提供;

二、技術架構

三:實現方案

接下來我們從五個部分來講具體的實現方案,分別爲DWD構建、維表Join、Flink Sink ClickHouse、異步 Load Batch HDFS、任務與數據質量監控;

1:DWD構建
主要包括六個功能,分別爲格式化、反作弊、去重、Imei-id 服務、Kafka sink exactly-once、分流。

  • 格式化

主要是將原始日誌按照 dwd 明細表的模型進行解析,並校驗各字段的準確和完整性;

  • 反作弊

主要是把線上測試渠道日誌以及實時反作弊策略命中的 IMEI 進行過濾;

  • 去重

主要是將客戶端產生日誌、服務端接收日誌、flume 同步日誌、kafka 流轉日誌這四個階段產生的重複日誌進行過濾,減少對業務分析和決策產生的影響;由於 APP 每日產生的用戶行爲日誌高達數百億條,所以如何做到高效精確去重?下面介紹兩種技術方案;

 方案一:redis cluster bitset 方案

實現方式是把每條日誌 hash 多次,通過管道方式向 redis 集羣請求 setbit 命令,通過布隆過濾的方式,判斷日誌是否重複。流程圖如下所示:  

       

此方案缺點是:重度依賴第三方 redis,需要解決 qps 高,redis 內存傾斜,redis 資源佔用大,以及整體架構的穩定性和擴展性等問題。

方案二:flink state 狀態去重方案

實現方式是使用 RocksDBStateBackend,開啓增量檢查點,存儲大狀態。根據日誌數據量和日誌 hash 衝突測試,設計日誌去重的 key。

time 可以根據自己的去重方案設計,比如 ‘20200601’ 或者 ‘2020060112’ 等動態時間戳。使用 ValueState<Boolean> ,可以判斷 distinctKey 是否存在。不存在代表不是重複日誌,存在代表是重複日誌。並且設置狀態的過期時間爲 24 小時。 

流程圖如下所示:

     

此方案基於 flink 強大的 state 功能,架構簡單可靠,程序處理速度與穩定性良好。

最終我們採用方案二來實現日誌去重的功能;

  • Imei-id 服務 

主要實現如下兩個功能:

  • 一是根據 IMEI 實時判斷新老用戶;

  • 二是把 IMEI 映射成一個正整數,支持使用 BitMap 計算用戶留存率;

下面分四步詳細介紹實現方案;

1)生成唯一 ID 方案對比:


2) 技術方案如下圖所示:根據上圖的方案比對,最終選擇了 wtable 方案。wtable可以根據 KV 存儲結構實時爲每個 Imei 生成唯一數字;

3) Operator 處理流程:

如上圖所示,利用 flink 狀態,避免每條日誌都去請求 wtable,提高處理速度。

4) 線程安全問題:

Imei 生成唯一數字 ID 服務,在一個 flink 任務中是線程安全的,可以保證 imei 對應唯一 ID;若有多個 flink 任務同時實現此服務則是不安全的。解決方案有如下兩個:

方案一:運用分佈式鎖,保證多進程之間數據安全。由於日誌數據量比較大,需要頻繁加鎖和釋放鎖,導致 Flink 程序處理邏輯比較複雜,程序處理性能低等問題;

方案二:根據 flink 的多流合併 api,把多個 kafka 數據源 Data stream 合併一個後,通過 key by imei 的方式來處理日誌並生成 ID,保證了線程安全,並且架構和代碼實現邏輯簡單;目前我們採用的是此方案。

  • Kafka sink exactly-once

爲了保證每條數據僅僅被處理一次,通過 Flink 兩階段提交語義來實現端到端一致性;

1)預提交階段

2)提交階段

3)代碼實現

1:開啓 check point,設置 CheckpointingMode.EXACTLY_ONCE;

2:輸出 kafka 版本需要 0.11  及以上;

3:enableCheckpointing(1000*60*5),checkpoint時間間隔爲5分鐘

4)手動恢復任務

1:如果程序需要重啓或代碼變更等情形下,從系統指定的savepoint目錄進行狀態恢復;

2:操作示例


  • 分流   

該功能主要目的是將APP公共行爲日誌按業務進行拆分,降低業務讀kafka流量,節省Flink計算資源。具體實現過程爲將公共行爲明細日誌關聯埋點報備維表,將關聯上的業務行爲日誌按照事先申請的topic進行分發。

2:維表 join

在實時數倉的建設過程中底層實時寬表的構建可謂是重中之重,如何保證實時寬表低延遲、高完整的產出是保證數據質量的關鍵,爲此我們整理了 DataStream 層面常用的三種維表 join 方式供大家參考;

1)在 RichFlatMapFunction 的 open 方法中讀取外部數據源,定時觸發更新,例如加載 mysql 維表數據,在 open 方法中先讀取指定維表信息,再將數據加載至內存,每次關聯是取內存中匹配對應的維表數據;

優點:

  • 實現方案簡單直接

缺點:

  • 維表更新時機不易把控可能存在 join 延遲

  • 需要評估維表數據量級以避免內存溢出

2)將維表數據加載到熱存儲中,如 redis、wtabe、hbase 等。通過第三方熱存儲來緩存維表數據,在數據關聯時直接用異步 IO 的方式從熱存儲獲取數據並關聯;

優點:

  • 維表量級不受限與內存可加載大量數據

  • 維表更新延遲較低(受限於維表數據寫入熱存儲)

缺點:

  • 需要額外熱存儲資源且佔用一定網絡開銷

3)將維表分爲全量維表和增量維表,程序啓動時加載全量維表到內存,採用 BroadcastState 將增量維表數據轉換爲流式數據,實時 Join 主體流與維表流;

優點:

  • 流式 Join 不依賴熱存儲實時更新維表數據

缺點:

  • 需要評估維表量級避免內存溢出

3:Flink Sink ClickHouse

如下圖是Flink 接入ClickHouse的流程圖 ,我們基於jdbc實現了高可靠的ClickHouse Sink,依據DataTime或DataSize實現了流數據低延遲導入CK;

4:異步 Load Batch HDFS

1:流程圖

2:實現方案

(1)採用10分鐘一個分區,按照 dt,hour,minute 層級目錄劃分;

(2)hdfs sink 的方案

  • 如果寫入的 hdfs 版本小於 2.7,則選擇 BucketingSink;

  • 如果寫入的 hdfs 版本大於等於 2.7,則選擇 StreamingFileSink 或者 BucketingSink;

  • 當前平臺的 hadoop 版本爲 2.6,故採用了 BucketingSink 方案;

(3)日誌實時寫入 hdfs 文件的方案

  • 目前是每分鐘數據落地到 hdfs 上;

  • 根據日誌量與 hadoop 設置的塊大小,目前採用的是 256M 落地到 hdfs 上;

  • 代碼示例:

sink.setBatchRolloverInterval(1 * 60 * 1000L);
sink.setBatchSize(1024 * 1024 * 256L)

(4)日誌落地 hdfs 時間分區的方案選擇

  •  根據日誌 EventTime 落地,數據寫入 EventTime 對應的時間分區目錄下,如圖:

    優點:數據分區準確,延遲的數據也會歸位到真實的時間分區下。

    缺點:若有延遲數據,每次查詢歷史分區,數據計算結果會發生變化。

  • 根據日誌 ProcessingTime 落地,數據寫入當前系統時間分區目錄下,如圖:

    

    優點:沒有日誌回溯問題,每次查詢歷史分區,數據計算結果不變。 

    缺點:如果發生數據延遲,數據只會追加到當前處理時間的分區目錄下,不能真實反映數據分佈情況。

  • 最終方案

經過線上任務實際測試,在正常情況下數據延遲率比較低,通常在秒級別,所以採用兩種時間結合的方案。即在 每天的 0 點 0 分-0 點 5 分時間段,採用 EventTime 時間處理,儘可能保證延遲數據不出現跨天的問題。其他時間段採用 ProcessingTime 時間處理,保證數據多次計算結果一致。

(5) Hdfs sink exactly-once

  • 開啓 check point,設置 CheckpointingMode.EXACTLY_ONCE;

  • hdfs sink 輸出的格式需要指定爲 text 格式;

  • 若需要重啓 flink 任務,需要指定上一次 savepoint 的 hdfs 文件路徑;

  • 由於 hadoop 2.6 版本不支持 truncate,flink 任務重啓後需要人工干預處理 hdfs 的文件,才能保證 hdfs sink 端到端一致性。

  • 編寫腳本,處理 flink 任務重啓後生成的 hdfs 文件;

(6)實時 hive 表與分區

  • 創建外部表,設置 location 爲日誌寫入 hdfs 的路徑;

  • 通過定時任務動態建立 dt、hour、minute 的分區; 

5:任務與數據質量監控  

1:實時任務監控,在大數據平臺自建的 Flink 開發平臺 Wstream 系統中,針對任務提供了 QPS、GC、CPU、Lag 延遲、內存使用等多維度的監控指標配置,一旦觸發規則進行實時報警,系統監控如圖所示:

2:實時數據質量監控

在實時數倉中,我們主要關注四個指標,分別是完整性,準確性,一致性,及時性,並按一分鐘粒度進行統計監控;

  • 完整性:指的是在 ETL 代碼中校驗日誌格式是否符合規範以及關鍵埋點是否存在,並實時將結果存儲到 DB 中,一旦觸發監控規則就進行報警;

  • 準確性:指的是在 ETL 代碼中校驗日誌關鍵埋點參數值是否正確,並實時將結果存儲到 DB 中,一旦觸發監控規則就進行報警;

  • 一致性:指的是在 ODS,DWD,DWS 層按一分鐘粒度計算各層 topic 讀入量,過濾量,輸出量,通過統計各分層的這三個值可以監控當前實時數倉在每一層流轉過程中數據條數的差異,從而保障實時數倉的一致性;爲了及時排查問題,我們將各層過濾日誌實時落地 HIVE 來加速問題定位;

  • 及時性:指的是監控各分層中 Flink 程序 ProcessingTime 和日誌 EventTime 的差值,通過該值來監控實時數倉的延遲率;

如下圖是其中一個質量指標監控效果圖:

       

進展與收益

  1. 在 1.0 階段,離線數倉採用 flume 傳輸到 Hdfs 後構建,實時數倉採用 kafka流轉並 進行計算;在 2.0 階段,通過 Flink 實時落地 Hive,我們在 ODS,DWD,DWS 層實現了離線和實時數倉的一致性,解決了 1.0 階段離線和實時數倉由於採集存儲通道不同,ETL 不一致,數據源不一致等幾個核心痛點。

  2. 通過在 DWD 用戶行爲明細層統一 ETL 和分流,將我們支持的部落,同鎮,廣告投放,遊戲,C 端 VIP 等多個業務進行流量日誌自動化拆分。由於各業務消費的是分流後的 topic,Flink 任務消費 kafka 流量下降了一個數量級,從而降低了 Flink 計算資源成本。

  3. 通過實時數倉分層建設,業務指標和計算口徑的變更,只需要在明細層的 ETL 任務中進行一次變更即可滿足下游所有任務,解決了之前需要多個計算任務同時變更的問題,從而保證了業務指標一致性。

  4. 通過基於 Flink 實時導入 ClickHouse 的各業務明細寬表,產品及分析師可以快速便捷的進行實時 Ad-Hoc 查詢,避免了 1.0 階段每個實時需求都需要排期開發,極大的提高了產品運營分析效率。

  5. 通過基於 Flink 實時數倉模型分層和元數據構建,各業務基於明細寬表通過 FlinkSQL 的方式就可以很快速的進行實時任務的開發,需求開發週期從周下降到天級別,極大的提升了開發效率。 

未來展望

從實時數倉 1.0 到 2.0,不管是數據架構還是技術方案,我們在深度和廣度上都有了更多的積累。隨着公司業務的快速發展以及新技術的不斷推出,實時數倉也會不斷的迭代優化。後續我們會從以下方面進一步提升實時數倉的服務能力。

  1. 實時數倉血緣關係的完善,提升任務和表的質量監控;

  2. Streaming SQL 持續改造和推進,可以有效減少jar文件開發方式的代碼維護成本;

  3. Flink 作業的監控,比如 checkpoint 的監控 ,目前 checkpoint 對開發者來說是黑盒,採用 exactly-once 會對實時程序的性能造成一定程度上的下降,所以該部分的持續優化和監控非常重要。

  4. 進一步強化實時任務的健壯性,包括自動化壓測以及極端情況下狀態的丟失等各種情況下的預案。

作者簡介:

曹德嵩,58同城分析與決策支持部資深開發工程師

李   輝,58同城分析與決策支持部高級開發工程師

參考文獻:

Flink官網(https://flink.apache.org/)

推薦閱讀:

基於Flink打造實時計算平臺爲企業賦能

Flink 1.11中對接Hive新特性及如何構建數倉體系

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