OnZoom 基於Apache Hudi的流批一體架構實踐

leesf發表於2021-12-26

1. 背景

OnZoom是Zoom新產品,是基於Zoom Meeting的一個獨一無二的線上活動平臺和市場。作為Zoom統一通訊平臺的延伸,OnZoom是一個綜合性解決方案,為付費的Zoom使用者提供建立、主持和盈利的活動,如健身課、音樂會、站立表演或即興表演,以及Zoom會議平臺上的音樂課程。

在OnZoom data platform中,source資料主要分為MySQL DB資料和Log資料。 其中Kafka資料通過Spark Streaming job實時消費,MySQL資料通過Spark Batch job定時同步, 將source資料Sink到AWS S3。之後定時排程Spark Batch Job進行數倉開發。最終按照實際業務需求或使用場景將資料Sink到合適的儲存。

初版架構問題

  • MySQL通過sql方式獲取資料並同步到S3是離線處理,並且某些場景下(比如物理刪除)只能每次全量同步
  • Spark Streaming job sink到S3需要處理小檔案問題
  • 預設S3儲存方式不支援CDC(Change Data Capture),所以只支援離線數倉
  • 因為安全要求,有時需求刪除或更新某個客戶資料時,只能全量(或指定分割槽)計算並overwrite。效能較差

2. 架構優化升級

基於以上問題,我們在進行大量技術調研選型及POC之後,我們主要做了如下2部分大的架構優化升級。

2.1 Canal

MySQL Binlog即二進位制日誌,它記錄了MySQL所有表結構和表資料變更。

Cannal基於MySQL Binlog日誌解析,提供增量資料訂閱和消費,將資料Sink到Kafka實現CDC。

後續使用Spark Streaming job實時消費Binlog就能解決上述問題1的時效性以及物理刪除等問題。

2.2 Apache Hudi

我們需要有一種能夠相容S3儲存之後,既支援大量資料的批處理又支援增加資料的流處理的資料湖解決方案。最終我們選擇Hudi作為我們資料湖架構方案,主要原因如下:

  • Hudi通過維護索引支援高效的記錄級別的增刪改
  • Hudi維護了一條包含在不同的即時時間(instant time)對資料集做的所有instant操作的timeline,可以獲取給定時間內的CDC資料(增量查詢)。也提供了基於最新檔案的Raw Parquet 讀優化查詢。從而實現流批一體架構而不是典型的Lambda架構。
  • Hudi智慧自動管理檔案大小,而不用使用者干預就能解決小檔案問題
  • 支援S3儲存,支援Spark、Hive、Presto查詢引擎,入門成本較低只需引入對應Hudi package

3. Hudi 實踐經驗分享

  1. Hudi upsert 時預設PAYLOAD_CLASS_OPT_KEY為OverwriteWithLatestAvroPayload,該方式upsert時會將所有欄位都更新為當前傳入的DataFrame。但很多場景下可能只想更新其中某幾個欄位,其他欄位跟已有資料保持一致,此時需要將PAYLOAD_CLASS_OPT_KEY傳為OverwriteNonDefaultsWithLatestAvroPayload,將不需要更新的欄位設為null。但該upsert方式也有一定限制,比如不能將某個值更新為null。

  2. 我們現在有實時同步資料,離線rerun資料的場景,但當前使用的是Hudi 0.7.0版本,該版本還不支援多個job併發寫Hudi表。臨時方案是每次需要rerun資料的時候暫停實時任務,因為0.8.0版本已經支援併發寫,後續考慮升級。

  3. 一開始我們任務變更Hudi表資料時每次都預設同步hive後設資料。但對於實時任務每次連線Hive Metastore更新後設資料很浪費資源,因為大部分操作只涉及到資料變更而不涉及表結構或者分割槽變動。所以我們後來將實時任務關閉同步hive後設資料,在需要更新後設資料時另外再執行hudi-hive-sync-bundle-*.jar來同步。

  4. Hudi增量查詢語義是返回給定時間內所有的變更資料,所以會在timeline在裡查詢歷史所有commits檔案。但歷史commits檔案會根據retainCommits引數被清理,所以如果給定時間跨度較大時可能會獲取不到完整的變更資料。如果只關心資料的最終狀態,可以根據_hoodie_commit_time來過濾獲取增量資料。

  5. Hudi預設spark分割槽並行度withParallelism為1500,需要根據實際的輸入資料大小調整合適的shuffle並行度。(對應引數為 hoodie.[insert|upsert|bulkinsert].shuffle.parallelism)

  6. Hudi基於parquet列式儲存,支援向後相容的schema evolution,但只支援新的DataFrame增加欄位的schema變更,預計在在 0.10 版本實現 full schema evolution。如果有刪除或重新命名欄位的需求,只能overwrite。另外增加欄位也可能導致hive sync metadata失敗,需要先在hive執行drop table。

  7. Hudi Insert 對 recordKey 相同的資料,根據不同的引數有不同的處理情況,決定性的引數包括以下三個:

    hoodie.combine.before.insert

    hoodie.parquet.small.file.limit

    hoodie.merge.allow.duplicate.on.inserts

    其中:hoodie.combine.before.insert 決定是否對同一批次的資料按 recordKey 進行合併,預設為 false;hoodie.parquet.small.file.limit 和hoodie.merge.allow.duplicate.on.inserts 控制小檔案合併閾值和如何進行小檔案合併。如果 hoodie.parquet.small.file.limit > 0 並且 hoodie.merge.allow.duplicate.on.inserts 為 false,那麼在小檔案合併的時候,會對相同 recordKey 的資料進行合併。此時有概率發生去重的情況 (如果相同 recordKey 的資料寫入同一檔案中);如果 hoodie.parquet.small.file.limit > 0 並且 hoodie.merge.allow.duplicate.on.inserts 為 true,那麼在小檔案合併的時候,不會處理相同 recordKey 的資料

4. 總結

我司基於Hudi實現流批一體資料湖架構上線生產環境已有半年多時間,在引入Hudi之後我們在以下各個方面都帶來了一定收益:

  • 成本: 引入Hudi資料湖方案之後,實現了S3資料增量查詢和增量更新刪除,之前更新刪除方案只能全表overwrite。Hudi實現智慧小檔案合併,之前需要單獨任務去處理。在資料處理和儲存方面都節約了相應成本,預估節省1/4費用。
  • 時效性: 所有ODS表已從T+1改造為Near Real Time。後續會建設更多實時表。
  • 效率: 在插入及更新資料時,預設情況下,Hudi使用Bloom Index,該索引更適合單調遞增record key,相比於原始Spark Join,其速度最高可提高10倍。查詢資料時,藉助Hudi提供的Clustering(將檔案按照某些列進行聚簇,以重新佈局,達到優化查詢效能的效果),Compaction(將基礎檔案和增量日誌檔案進行合併,生成新版本列存檔案)等服務,可將查詢效能提升50%+

最後,誠懇邀請對建設流批一體資料湖感興趣的同學加入我們團隊,工作地在杭州,有想法的小夥伴可以加微信 sky000a 或直接傳送簡歷到郵箱 zing.mao@zoom.us

相關文章