愛奇藝數據湖實戰 - 實時湖倉一體化

01

   概述

數據是洞察用戶、市場、運營決策的基礎資料,在愛奇藝被廣泛應用在推薦、廣告、用戶增長、營銷等場景中。愛奇藝大數據業務之前採用 Lambda 架構,滿足海量數據處理、時效性等方面需求,但開發維護及資源成本高,同時還存在數據孤島問題。最近幾年興起的以 Iceberg、Hudi、Delta Lake 爲代表的數據湖技術爲構建統一的數據架構提供了基礎。愛奇藝大數據團隊在 2020 年引入 Iceberg 作爲數據湖基座,並基於 Iceberg + Flink 構建了流批一體化數據生產架構替代傳統的 Lambda 架構,打造了實時湖倉,已在 BI 數倉、會員、推薦、日誌等多種場景落地。當前每日處理 PB 級的數據,延遲從小時級降低到 1 至 5 分鐘,大幅提升數據流通效率。本文介紹了愛奇藝流批一體架構及實時湖倉建設實踐過程。


02

   傳統 Lambda 架構:離線 + 實時兩套數據生產鏈路


圖 1 Lambda 架構

愛奇藝採用數據集成、數據倉庫、數據開發的典型數據架構,將埋點、後端日誌、數據庫、指標等來源的數據應用到推薦、搜索、營銷、報表等場景中。整個數據架構的建設分爲兩條鏈路,如圖 1 所示:

  1. 離線通路:使用 HDFS、Hive、Spark 等工具,按 ODS、DWD、DWS 分層的數倉架構構建了離線數倉。通過 Venus 日誌採集、MySQLIO CDC 同步、Hubble 監控平臺等工具將不同數據源集成到離線數倉,同時提供魔鏡離線分析、Babel 離線計算兩個開發平臺支持數據應用開發。數倉中的 Hive 表按小時分區,從數據產生到業務場景應用通常有小時級的延時。
  2. 實時通路:數據價值隨生命週期增長而迅速衰減,離線通路的延遲導致業務無法快速利用數據的價值,因此又構建了實時通路。參考離線數倉,使用 Kafka 和 Flink 搭建實時流式數倉。Venus、MySQLIO、Hubble 等數據集成工具支持不同數據源集成到實時流式數倉。在數據開發層面,新增 RCP 實時計算平臺、RAP 實時分析平臺,用於支持實時數據應用開發。實時數據通路可以達到秒級延時。

上述 Lambda 數據架構雖然可以滿足業務場景需求,但帶來了如下問題:
  1. 系統複雜度高:服務提供方需要提供多種組件,業務需要學習多個組件,並開發維護離線、實時兩套程序。
  2. 數據一致性難保障:實時、離線兩套代碼,要做到數據處理邏輯統一、數據一致,存在較大挑戰。
  3. 資源成本高:數據鏈路多個環節存在兩套重複的存儲和計算,成本較高。
  4. 數據延時大:實時數據雖然延時低,但是存儲時間短,數據查詢分析需求仍然依靠離線數據,具有 T+1 的延時。

爲了解決上述這些問題,我們基於數據湖技術對數據生產鏈路進行了流批一體化改造,並構建了實時湖倉。

03

   實時湖倉一體化

數據湖簡介
在介紹實時湖倉之前,先簡單介紹下數據湖(更多背景介紹可以參看之前發表的文章《 愛奇藝數據湖實戰 》)。愛奇藝基於 Iceberg 構建了數據湖,使用 HDFS 作爲底層存儲、Alluxio 作爲緩存層,Iceberg 作爲表格式工具,表的元數據通過 Hive metastore 管理,如圖 2 所示。業務在不同的開發平臺上,使用 Spark、Flink、Trino 等計算引擎查詢、處理湖數據。

圖 2 愛奇藝數據湖

Iceberg 是讀寫數據湖的入口,也是實時湖倉架構最重要的組件,其架構如下:

圖 3 Iceberg 架構

Iceberg 分爲數據層、元數據層和 Catalog 層。Catalog 層是表的統一入口,數據層保存實際數據,在我們的數據湖架構中,Catalog 層爲 Hive metastore,數據層數據文件存在 HDFS 上。元數據層主要存放表的 schema、分區、及每列的最大值、最小值等統計信息。Iceberg 通過 snapshot 進行讀寫隔離,每個寫入週期對應一個 snapshot,新數據先寫入到新的數據文件,再生成元數據文件,通過 commit 操作生成一個 snapshot,原子性的更新到 Catalog,commit 成功的 snapshot 才能讀取。

Iceberg 具備如下特性:
  • 可控的數據延時:snapshot 的生成速度決定了數據生成的延時,snapshot 生成越快,數據延時越低,最快可以到 1 分鐘以內。
  • 流批一體:Iceberg 既支持 Spark、Flink 引擎以批模式讀寫數據,也支持以流模式讀寫數據,能做到存儲層面的流批一體。
  • 資源成本低:底層存儲在 HDFS 上,支持列存儲及數據壓縮,理論上與 Hive 的存儲成本相當,遠小於 Kafka 的存儲成本。Iceberg 在元數據層加入了統計信息,可用於查詢優化,查詢成本比 Hive 低。
  • 支持數據變更:Iceberg V2 格式的表支持行級數據變更,可以更好的集成數據庫數據。
基於如上特性,Iceberg 可以完全替代 Hive,並在分鐘級延時的場景中替代 Kafka。

實時湖倉一體化架構

圖 4 實時湖倉一體架構

基於 Iceberg 和 Flink,我們構造瞭如圖 4 所示的實時湖倉一體架構。ODS、DWD、DWS 每個層面的數據都存儲在 Iceberg,每層的數據通過 Flink 流式消費上游數據實時生產。數倉的每張表既可以被 RAP 實時分析平臺、RCP 實時計算平臺流式實時處理,也可以被魔鏡離線分析平臺、Babel 離線計算平臺以批模式處理。在數據集成方面,Venus 日誌採集、MySQLIO CDC 同步、Hubble 監控支持將日誌或指標類數據寫入 Iceberg。依賴 Iceberg 支持行級數據變更的特性,藉助 Flink CDC 可以實現數據庫的全量數據實時同步。實時湖倉的數據架構具備如下特點:
  • 湖倉一體:既具備數據湖的靈活性,也具備數倉的結構化數據管理能力,可以統一存儲結構化、半結構化、非結構化的數據,形成統一的數據底座,消除數據孤島。
  • 流批一體:將 Lambda 架構下的實時、離線兩條數倉生產通路合併成一條,數倉的每個層次生產一份數據,既能用於按小時、天讀數據的批計算,也能用於流式消費的實時計算。一套數倉避免了開發兩套代碼、計算邏輯對齊的問題,能大幅提高數據開發效率
  • 資源成本低:相比於實時、離線兩個數倉生產通路,流批一體的實時湖倉的數據生產、存儲成本更低。相比Hive,Iceberg 元數據層更豐富的統計信息,也有助於查詢性能提升,降低查詢成本。
  • 延時低:Iceberg 支持分鐘級的數據可見性,通過實時寫入 Iceberg,實時湖倉可以達到分鐘級的延時。數據的使用方不需要在應用側採用流、批數據融合的方式支持最新的全量數據,簡化業務側處理邏輯。


挑戰

實時湖倉架構雖然有較大收益,但結合愛奇藝的數據體系,在生產環境應用,需要應對如下挑戰:
  • 單任務生產多表問題:在愛奇藝的埋點、容器日誌等數據源中,不同業務的日誌混合在一起,需要按日誌特徵拆分到不同業務的 ODS 層表。單個任務消費一個數據源可能拆分出 500 多張表,最大的任務需要拆分到3000 多張表,總共拆分到上萬張表。當前的 Flink Iceberg Sink 僅支持寫入到一張表,而一個 Flink 任務也無法添加如此多的 Iceberg Sink。因此,如何解決單任務生產多表的問題是我們面臨的首要難題。
  • 數據生產進度評估:需要在實時生產數據的過程中評估數據生產進度,以便數據的使用方瞭解湖倉中數據的完整度,觸發下游批任務。
  • 流式消費的積壓監控:對於下游的流式消費任務,需要像消費 Kafka 一樣給出消費積壓監控。
  • 數據完備性保障:在 Lambda 架構下,在數據通路故障時,可以通過重新調度批計算修正數據,保證數據的最終完備性。實時湖倉架構也需要有保證數據完備性的機制。
接下來介紹下我們應對上述挑戰的方案。



04

   解決方案

單任務生產多表


圖 5 原生 Flink Iceberg Sink

原生的 Flink Iceberg Sink,包含一個 Writer 算子,一個 Committer 算子,如圖 5 所示。Writer 算子接收的 DStream 中的數據全都屬於一張表,每個 Writer 算子的 Task 將數據寫入 Iceberg 數據文件後,在 Checkpoint 時,將文件信息封裝在 WriterResult 發送給 Committer 算子。Committer 算子並行度爲 1,只有一個 Task,收集到 Writer 算子全部 Task 發送的 WriterResult 後,提交文件信息給 Iceberg 表生成一個新的 Snapshot。原生的 Flink Iceberg Sink 僅支持寫入一張表,如果寫入多個表,需要添加多個 Sink 算子,如果太多,Flink Job 就會無法運行。因此,爲了支持一個任務同時寫大量表的場景,我們自研了支持多表寫入的 Flink Iceberg Sink,如圖 6 所示。

圖 6 支持多表寫入的 Flink Iceberg Sink

相比原生 Flink Iceberg Sink,主要改造點如下:
  1. 定義 MultiTableRow 類型。相比 Row,增加了所屬的 Table 名稱。
  2. 爲避免每個 Writer 算子的 Task 寫入過多表,出現小文件過多、內存使用過大及性能問題,在 Writer 算子之前加入 Partitioner 算子。通過 Partitioner 算子將相同表的數據路由到固定幾個 Writer Task,一個 Writer Task 只處理部分表的寫入。
  3. Writer 算子基於 MultiTableRow 中的表名字加載表,將數據寫入到對應表的文件。在 Checkpoint 時,首先按表名彙總各表新寫入的文件構建 MultiTableWriteResult 對象,MultiTableWriteResult 對象相比 WriterResult 增加了表名信息。然後按表名 Shuffle 後發送給 Committer 算子。
  4. Committer 算子的並行度不爲 1,爲默認並行度。在 Checkpoint 時,每個 Committer 算子的 Task 基於收到的MultiTableWriteResult 彙總各表的寫入文件,提交到對應的表生成新的 Snapshot。


圖 7 使用 MultiTable Flink Iceberg Sink 生產多表並自動負載均衡

在使用多表寫入的 Flink Iceberg Sink 時,設計合適的 Partitioner 非常關鍵,如果實現不當,可能導致 Writer 算子的各 Task 負載不均衡。如圖 7 所示,在我們的 Partitioner 實現中,基於實時統計的各表新寫入數據的大小,及 Flink 任務分配的 Slot 數量動態生成 Shuffle 策略。大表的數據路由到多個 Writer Task,多個小表數據路由到一個 Writer Task,保證每個 Writer Task 處理的數據量基本均衡,達到最佳寫入性能,也能降低小文件生成數量。使用多表寫入的 Flink Iceberg Sink,我們的一個線上任務生產了 3000 多張表,多個任務生產了 500 多張表。

數據生產進度評估

批任務通常基於消費表的分區級數據完整度觸發計算,如一個計算表每小時數據的批任務,需要在每小時的數據完備後觸發。因此,Iceberg 也要像 Hive 表一樣提供分區完整度。我們通過改造 Flink Iceberg Sink 實現,針對原生的單表 Sink,我們的改造方案如圖 8 所示,我們自研的多表 Sink 的改造方案類似。

圖 8 Iceberg 表生產進度評估

Flink Iceberg Sink 負責數據的寫入,內部包含兩個算子:Writer 和Committer。Writer 算子的 Task 負責將數據寫入Iceberg 的數據文件,在寫入數據的同時,基於指定時間列的值,記錄遇到的最大時間 max(ts)。在 Checkpoint 時,每個 Writer Task 把 max(ts) 及新寫入的文件,一起傳給 Committer,Committer 提交文件生成新 Snapshot 的同時,將接收的 max(ts) 中的最小值減去允許的延時時間作爲 Snapshot 對應的 Watermark,記錄到 Snapshot 的Summary 中。數據湖平臺週期性的讀取表的 Snapshot Summary 中 Wartermark,判斷如果屬於新的一小時,表示上一小時分區的數據已經完備,將完備信息提交給進度管理服務,進而觸發離線計算平臺上的對應批任務。

流式消費積壓監控

在流計算場景中,數據生產延時是一個重要的數據質量指標。流式消費 Kafka,Flink Kafka Souce 通過提交消費Offset,藉助專門的服務(如 Kafka Manager、Burrow)計算生產的 offset 與消費提交的 offset 的差值評估消費積壓,進而評判數據生產是否有延時。流式消費 Iceberg 也需要有類似的機制,社區版 Flink Iceberg Source 沒有類似功能,需要改造實現。
Flink Iceberg Source 基於 Flink FLIP-27 中的 Source Interface 實現,如圖 9 所示。該接口中有兩個重要的組件:Split Enumerator 和 Reader,功能如下。
  • Split Enumerator:發現 Iceberg 表的新 Snapshot,讀取 Snapshot 中的文件,並切分成 Split 塊。
  • Reader:接收 Split Enumerator 分配的 Split 塊,讀取 Split 塊中的數據,發送到下游算子。


圖 9 Flink Source Interface 核心組件

Iceberg 的 Snapshot 是數據寫入及消費獲取新文件的最小粒度,我們的方案是在任務內,計算當前消費 Snapshot 的延時。具體實現是,在 Split Enumerator 組件內,用當前時間減去新獲得的 Snapshot 的生成時間作爲延時指標集成到 Flink 的 Metrics 體系。Flink 的 Metrics 已經對接到基於 Prometheus+Grafana 的監控報警平臺,可以方便地配置延時監控及報警。

數據完備性保障

實時湖倉的數據通過 Flink 流任務實時生產,在組件服務故障,或者代碼 bug 導致數據丟失時,如何修正數據,保證數據的完備性?流任務雖然可以回溯重新計算數據,但會造成新數據處理延時,也很難確定回溯的準確位置導致數據仍可能有重複或丟失,同時還可能遇到上游數據已過期刪除的問題。流任務都在 RCP 實時計算平臺上開發,我們的方案是拓展 RCP 實時計算平臺,支持一個 Flink 應用既可以以流模式運行,也可以以批模式運行,使用批任務修正流任務的結果,如圖 10 所示。

圖 10 數據修正

正常情況下,應用只以流模式運行持續生產數據。當數據有問題時,保持流模式作業運行的情況下,新調度一個批模式運行的任務,按表分區修正下游表的數據。RCP 也支持定時調度批任務,可以用每天的批計算結果覆蓋流計算結果。批任務與流任務的運行時參數和配置會有差別,RCP 支持對 SQL 開發應用配置流、批運行模式下使用不同的數據源及運行配置。同時,在啓動任務時,通過 JVM 參數傳遞當前的運行模式,以便任務基於不同的模式調整執行邏輯。基於 RCP 流批一體功能,可以在不影響流任務的情況下實現批任務使用同構、異構數據源修正流任務生產的結果表。


05

   落地效果

實時湖倉一體化架構推出後,已在如下場景落地:
  • Venus:Venus 是愛奇藝的日誌平臺,負責後端日誌採集、存儲、分析。原來日誌統一存儲在 Elasticsearch,與大數據體系割裂,現在大部分日誌都統一入湖,對應實時湖倉的 ODS 層表。容器類日誌原來先採集到統一的Kafka topic,然後按業務分割到業務 topic,再寫入 Elasticsearch。遷移到實時湖倉架構後,從統一 Kafka topic 按業務拆分後直接寫入 Iceberg,省掉了業務 topic 環節。Venus 總共寫入超過 1 萬張 Iceberg 表,每日寫入 PB 級日誌,延時 5 分鐘,每年節省上千萬成本。Venus 入湖改造的詳細介紹見之前發佈的文章《 愛奇藝數據湖實戰 - 基於數據湖的日誌平臺架構演進
  • Pingback:Pingback 是愛奇藝埋點數據的統稱,大部分埋點數據需要經過數倉架構 ODS、DWD、DWS 分層處理。我們按照數倉層次從前到後、業務重要程度從低到高的順序使用實時湖倉 Iceberg 表替代 Hive 表。同時,也推動了部分能接受分鐘級延時的業務從 Kafka 數據遷移到 Iceberg 表。目前已上線 1300 多張 Iceberg 表,每日新增數百 TB 數據,每一層的 Iceberg 表最低延時 1 分鐘。
  • 數據庫數據:Flink CDC 支持全量、增量數據的透明切換並實時寫入 Iceberg,數據庫數據入湖統一切換成了 Flink CDC。目前已同步廣告、會員、用戶增長等業務的近百張表。

06

   未來規劃

實時湖倉一體化架構已經在實踐中得到了充分驗證,在節省成本、提高數據時效性、降低數據複雜性等方面都取得了較大收益,之後規劃如下:
  1. 繼續上線更多業務場景,完全替代 Hive,並替代接受分鐘級延時的 Kafka 數據。
  2. 將 Flink CDC 從 2.x 升級到 3.x,支持 Schema 自動變更,降低數據庫數據入湖的維護代價。
  3. Iceberg 不支持更新部分列、基於變更數據繼續構建 Pipeline 等功能,限制了一些應用場景,正在引入新興的數據湖技術 Paimon 作爲這類場景的替補。


數據湖在愛奇藝數據中臺的應用

愛奇藝數據湖實戰 - Hive數倉平滑入湖

愛奇藝數據湖實戰 - 基於數據湖的日誌平臺架構演進




本文分享自微信公衆號 - 愛奇藝技術產品團隊(iQIYI-TP)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

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