愛奇藝基於 Hive 構建了傳統的離線數據倉庫,支持了公司運營決策、用戶增長、視頻推薦、會員、廣告等業務需求。近幾年,隨着業務對數據實時性的更高要求。我們引入了基於 Iceberg 的數據湖技術,大幅提升數據查詢性能及整體流通效率。從性能和成本角度考慮,將現有的Hive表遷移到數據湖是必要的。然而多年來,大數據平臺上已經積累了數百 PB 的 Hive 數據,如何將 Hive 遷移到數據湖,成爲我們面臨的一大挑戰。本文介紹了愛奇藝從 Hive 平滑遷移到 Iceberg 數據湖的技術方案,幫助業務加速數據流程,提效增收。
01
Hive VS Iceberg
Hive 是一個基於 Hadoop 的數據倉庫和分析平臺,提供了類似 SQL 的語言,支持複雜的數據處理和分析。
Iceberg 是一個開源的數據表格式,旨在提供可擴展、穩定和高效的表格存儲,以支持分析性工作負載。Iceberg 提供了類似傳統數據庫的事務性保證和數據一致性,並支持複雜的數據操作,如更新、刪除等。
表 1-1 分別列出了 Hive 和 Iceberg 在時效性、查詢性能等方面的比較情況:
表 1-1 Hive 和 Iceberg 的對比
切換到 Iceberg 可以提高數據處理的效率和可靠性,爲複雜的數據操作提供更好的支持,目前已接入廣告、會員、Venus 日誌、審覈等十幾個業務。關於愛奇藝 Iceberg 實踐的更多細節,可閱讀之前的系列文章(見文末引用)。
02
Hive 存量數據平滑切換 Iceberg
Iceberg 相比 Hive 有諸多優勢,可是業務的數據已經運行在 Hive 環境中,業務不希望投入大量的人力修改存量的任務。我們調研了業界常用切換方法 [1],在數據湖平臺上提供了自助式 Hive 平滑切換 Iceberg 的能力,本節將闡述具體實現方案。
1. 查詢兼容性
在實際切換前,我們驗證了 Spark 對 Hive 和 Iceberg 的兼容性。
Spark 對 Hive 和 Iceberg 表的查詢、寫入語法基本是相同的,對 Hive 表的查詢 SQL 語句無需修改即可查詢 Iceberg 表。
但 Iceberg 與 Hive 在 DDL 方面存在較大差異,主要表現在對錶結構進行修改的處理方式上,詳細信息如表 2-1 所述。實際 schema 與數據文件的 schema 需要一一對應,否則會影響數據的查詢,因此對於 DDL 語句的處理應該更爲謹慎,不建議將這類 DDL 語句與任務綁定在一起。
表 2-1 Hive 和 Iceberg 語法兼容性對比
2. 業界切換方案
2.1 業務雙寫切換
業務複製現有的pipeline,實現Hive、Iceberg雙寫。待新舊通路對數一致後,切換到 Iceberg 通路,並下線原有的通路。該方案需要業務投入人力進行開發、對數,耗時耗力。
2.2 原地切換,客戶端停寫
如果業務允許停寫一段時間進行切換,則可以用如下一些方式:
-
Spark migrate procedure 是 Iceberg 官方提供的函數,可將一個 Hive 表原地切換爲 Iceberg,示例如下:
CALL catalog_name.system.migrate('db.sample'); |
該程序不會修改原始數據,僅會掃描原表的數據然後構建 Iceberg 元信息,引用原始的文件。因而 migrate 程序執行速度非常快,但存量數據無法利用文件索引等特性加速查詢。如希望存量數據也加速,可以使用 Spark 的rewrite_data_files 方法重寫歷史數據。
migrate 程序並不會將 Hive 表刪除,而是將其重命名爲 sample__BACKUP__,此處 __BACKUP__ 後綴是硬編碼的,如果需要回滾可將新建的 Iceberg 表 Drop 掉,將 Hive 表 rename 回去。
-
使用 CTAS 語句,Spark 示例如下:
CREATE TABLE db.sample_iceberg USING Iceberg PARTITIONED BY dt LOCATION 'qbfs://....' TBLPROPERTIES('write.target-file-size-bytes' = '512m', ...) AS SELECT * FROM db.sample; |
在寫入完成後進行對數,符合要求後,通過重命名完成切換。
ALTER TABLE db.sample RENAME TO db.sample_backup; ALTER TABLE db.sample_iceberg RENAME TO db.sample; |
CTAS 相比於 migrate 優勢是存量數據重新寫入,因而可以優化分區、列排序、文件格式和小文件等。缺點是如果存量數據較多,重寫耗時耗資源。
以上兩個方案,具有如下特性:
優點:
方案簡單,執行已有的 SQL 即可
可回滾,原 Hive 表還在
缺點:
寫入/讀取程序未驗證:切換到 Iceberg 表後,可能出現寫入或查詢異常
要求切換過程停寫,對一些業務是不能接受的
3. 愛奇藝平滑遷移方案
考慮到上述方案的缺點,我們設計了原地雙寫 + 透明切換的方案,實現平滑遷移,如圖 2-1 所示:
-
建表:創建與 Hive 相同 schema 的 Iceberg 表,同步 Hive 表的 TTL、權限等元信息到 Iceberg 表。 -
歷史數據遷移到 Iceberg : Hive 歷史數據通過 add_file procedure 添加到 Iceberg 中,該操作會根據 Hive 數據構建出 Iceberg 的元數據,實際上 Iceberg 的元數據中指向的是 Hive 的數據文件,減少了數據冗餘及歷史數據同步時間。 -
增量數據雙寫 : 通過愛奇藝自研的 Pilot SQL 網關探測 Hive 表的寫入任務,自動複製寫入 SQL,並將輸出替換成 Iceberg 表,實現雙寫。 -
數據一致性 校驗: 當歷史數據同步完成且增量雙寫到一定次數之後,後臺會自動發起對數,校驗 Hive 和 Iceberg 中的數據是否一致。對於歷史數據與增量數據會選取一部分數據進行 count 以及字段 CRC 數值校驗。 -
切換 : 數據一致性校驗完成後,進行 Hive 和 Iceberg 的切換,用戶不需要修改任務,直接使用原來的表名進行訪問即可。正常切換過程耗時在幾分鐘之內。
03
核心收益 - 加速查詢
1. Iceberg 查詢加速技術
2. Iceberg 加速技巧
-
配置分區:使用分區剪裁的方式使查詢只針對特定分區的數據執行,而不需要掃描整個數據集。 -
指定排序列:通過對數據分佈進行合理的組織,最大限度的發揮文件級別的過濾效果,使得查詢只集中在特定的文件。例如通過下面的方式使得寫入 sample 表的數據按照 category, id 降序寫入,注意由於多了一個排序的環節,這種方式會比非排序的寫入耗時長。
|
-
高基數列應用布隆過濾器:在查詢數據時,會自動應用布隆過濾器來快速驗證查詢數據是否存在於某個數據塊,避免不必要的磁盤訪問。
|
-
使用 Trino 代替 Spark:由於 Trino 自身 MPP 的架構,在查詢上相較於 Spark 更有優勢,並且 Trino 自身對 Iceberg 也有相應的優化,因此如果有秒級查詢的需求,可將引擎由 Spark 切換到 Trino。 -
Alluxio 緩存:使用 Alluxio 作爲數據緩存層,將數據緩存在內存中。在查詢時可以直接從內存中獲取數據,避免從磁盤讀取數據的開銷,可大大提高查詢速度,也可防止 HDFS 抖動對任務的影響。 -
ORC 代替 Parquet:由於 Trino 對 ORC 格式有特定的優化,使得 ORC 的讀取性能要優於 Parquet,可以將文件格式設置爲 ORC 加速查詢。 -
配置合併:寫 Iceberg 的任務往往會出現寫入文件較小但數量較多的情況,通過將小文件合併成一個或少量更大的文件,有利於減少讀取的文件數,降低磁盤 I/O。
3. 性能評測
3.1 文件內過濾性能提升
3.2 列排序對文件內過濾性能提升
-
同樣的文件格式,排序後文件內過濾效果更好,大致能快 40%; -
ORC 查詢性能優於 Parquet; -
使用 Trino 查詢,我們推薦 Iceberg 表 + ORC 文件格式 + 列排序;
3.3 列排序對文件級過濾性能提升
|
-
按照 prov 排序查詢讀取數據量是不排序的 25%,耗時是 66%; -
按照 isp 排序提升不明顯,這是因爲 isp 數據量有明顯的傾斜,條件中 isp 值佔比高達 90%;
3.4 布隆過濾器的性能提升
3.5 Spark 和 Trino 性能比較
-
Trino 對於 V2 表查詢結果與 Spark 一致,且在相同核數性能優於 Spark,耗時是 Spark 的 1/3 左右; -
隨着變更輪次的增加(Data File 和 Postition Delete File 數量增加),Trino 查詢性能也會逐漸變慢,需要定期進行合併。
04
核心收益 - 支持變更
1. 變更在業務使用場景
-
ETL 計算:如廣告計費,通過接入 Iceberg 實現變更,簡化業務邏輯,實現了更長時間範圍的轉化回收; -
數據修正:批量修正,如對某個數據的狀態進行修改、批量刪除等; -
隱私相關:如播放記錄、搜索記錄,用戶需要刪除歷史條目等; -
CDC 同步:如訂單業務,需要將 MySQL 中的數據進行大數據分析,通過 Flink CDC 技術很方便地將 MySQL 數據入湖,實時性可達到分鐘級。
2. Hive 如何實現變更
-
分區覆寫 例如修改某個 id 的相關內容,先篩選出要修改的目標行,更新後與歷史數據進行合併,最後覆蓋原表。這種方式對不需要修改的數據進行了重寫,浪費計算資源;且覆寫的粒度最小是分區級別,數據無法進一步細分,任務耗時相對較長。 -
標記刪除 通常的做法是添加標誌位,數據初始寫入時標誌位置 0,需要刪除時,插入相同的數據,且標誌位置 1,查詢時過濾掉標誌位爲 1 的數據即可。這種方式在語義上未實現真正的刪除,歷史數據仍然保存在 Hive 中,浪費空間,而且查詢語句較爲複雜。
3. Iceberg 支持的變更類型
-
Delete:刪除符合指定條件的數據,例如
|
-
Update:更新指定範圍的數據,例如
|
-
MERGE:若數據已存在 UPDATE,不存在執行 INSERT,例如
|
4. Iceberg 變更策略
-
Copy on Write(寫時合併):當進行刪除或更新特定行時,包含這些行的數據文件將被重寫。寫入耗時取決於重寫的數據文件數量,頻繁變更會面臨寫放大問題。如果更新數據分佈在大量不同的文件,那麼更新的執行速度比較慢。這種方式由於結果文件數較少,讀取的速度會比較快,適合頻繁讀取、低頻批次更新的場景。 -
Merge on Read(讀時合併):文件不會被重寫,而是將更改寫入新文件,當讀取數據時,將新文件合併到原始數據文件得到最終結果。這使得寫入速度更快,但讀取數據時必須完成更多工作。寫入新文件有兩種方式,分別是記錄刪除某個文件對應的行(position delete)、記錄刪除的數據(equality detete)。 -
Position Delete:當前 Spark 的實現方式,記錄變更對應的文件及行位置。這種方式不需要重寫整個數據文件,只需找到對應數據的文件位置並記錄,減少了寫入的延遲,讀取時合併的代價較小。 -
Equality Delete:當前 Flink 的實現方式,記錄了刪除數據行的主鍵。這種方式要求表必須有唯一的主鍵,寫入過程無需查詢數據文件,延遲最低;然而它的讀取代價最大,這是由於讀取時需要將 equality delete 記錄和所有的原始文件進行 JOIN。
表 4-1 Iceberg 不同變更策略對比
表 4-2 Iceberg 變更屬性配置方式
5. 業務接入
5.1 廣告計費轉換
-
每天觸發一次計算,從行爲表聚合出過去 7 天的“計費時間”數據。此處用 rt 字段代表計費時間 -
提供統一視圖合併行爲數據和計費時間數據,計費歸因表 rt as dt 作爲分區過濾查詢條件,滿足同時檢索曝光和計費轉化的需求
|
-
時效性提升:從天級縮短到小時級,客戶更實時觀察成本,有利於預算引入; -
計算更長週期數據:原先爲計算效率僅提供 7 日內轉換,而真實場景轉換週期可能超過 1 個月; -
表語義清晰:多表聯合變爲單表查詢。
5.2 數據修正
|
05
總結
06
引用
-
From Hive Tables to Iceberg Tables: Hassle-Free -
通過數據組織優化加速基於Apache Iceberg的大規模數據分析 -
Row-Level Changes on the Lakehouse: Copy-On-Write vs. Merge-On-Read in Apache Iceberg -
《愛奇藝數據湖實戰 - 綜述》 -
《愛奇藝數據湖實戰 - 廣告》 -
《愛奇藝數據湖實戰 - 基於數據湖的日誌平臺架構演進》 -
《愛奇藝數據湖實戰 - 數據湖技術在愛奇藝BI場景的應用》 -
《愛奇藝在Iceberg落地相關性能優化與實踐》
本文分享自微信公衆號 - 愛奇藝技術產品團隊(iQIYI-TP)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。