醫療在線OLAP場景下基於Apache Hudi 模式演變的改造與應用

背景

在 Apache Hudi支持完整的Schema演變的方案中(https://mp.weixin.qq.com/s/rSW864o2YEbHw6oQ4Lsq0Q), 讀取方面,只完成了SQL on Spark的支持(Spark3以上,用於離線分析場景),Presto(用於在線OLAP場景)及Apache Hive(Hudi的bundle包)的支持,在正式發佈版本中(Hudi 0.12.1, PrestoDB 0.277)還未支持。在當前的醫療場景下,Schema變更發生次數較多,且經常使用Presto讀取Hudi數據進行在線OLAP分析,在讀到Schema變更過的表時很可能會產生錯誤結果,造成不可預知的損失,所以必須完善Presto在讀取方面對Schema完整演變的支持。

另外用戶對使用presto對Hudi讀取的實時性要求較高,之前的方案裏Presto只支持Hudi的讀優化方式讀取。讀優化的情況下,由於默認的布隆索引有如下行爲:

  1. insert 操作的數據,每次寫入提交後能夠查詢到;
  2. update,delete操作的數據必須在發生數據合併後才能讀取到;
  3. insert與(update,delete)操作 presto 能夠查詢到的時間不一致;
  4. 所以必須增加presto對hudi的快照查詢支持。

由於Presto分爲兩個分支(Trino和PrestoDB),其中PrestoDB的正式版本已經支持快照查詢模式,而Trino主線還不存在這個功能,所以優先考慮在PrestoDB上實現,我們基於Trino的方案也在開發中。

計劃基於Prestodb的Presto-Hudi模塊改造,設計自 RFC-44: Hudi Connector for Presto。單獨的Hudi連接器可以拋開當前代碼的限制,高效地進行特定優化、添加新功能、集成高級功能並隨着上游項目快速發展。

術語說明

  • read_optimized(讀優化):COW表和MOR表的ro表,只讀取parquet文件的查詢模式

  • snapshot(快照):MOR表的rt表,讀取log文件和parquet並計算合併結果的查詢模式

現狀:

Hudi的Schema演變過程中多種引擎的表現

其中trino是以官方360版本爲基礎開發的本地版本,部分參考某打開狀態的pr,使其支持了快照查詢

Hive對Hudi支持的情況

hive使用hudi提供的hudi-hadoop-mr模塊的InputFormat接口,支持完整schema的功能在10月28日合入Hudi主線。

Trino對Hudi支持的情況

Trino版本主線分支無法用快照模式查詢。Hudi連接器最終於22年9月28日合入主線,仍沒有快照查詢的功能。本地版本基於trino360主動合入社區中打開狀態的pr(Hudi MOR changes),基於hive連接器完成了快照查詢能力。

PrestoDB對Hudi支持的情況

PrestoDB版本主線分支支持Hudi連接器,本身沒有按列位置獲取列值的功能,所以沒有串列問題,並且支持快照查詢模式。

改造方案

版本

  • Hudi: 0.12.1

  • Presto: 0.275

該模塊的設計如下

讀優化

Presto 會使用它自己優化的方式讀parquet文件。在presto-hudi的HudiPageSourceProvider -> HudiParquetPageSources -> 最終使用presto-parquet 的 ParquetReader讀取

快照

Presto 針對mor表的快照讀,會使用hudi提供的huid-hadoop-mr的InputFormat接口。在presto-hudi的HudiPageSourceProvider -> HudiRecordCursors裏創建 HoodieParquetRealtimeInputFormat -> 獲取RealtimeCompactedRecordReader,基礎文件使用HoodieParquetInputFormat的getRecordReader,日誌文件使用HoodieMergedLogRecordScanner掃描

讀優化的改造

基本思想:在presto-hudi模塊的HudiParquetPageSources中,獲取文件和查詢的 InternalSchema ,merge後與presto裏的schema列信息轉換,進行查詢。

具體步驟:

  1. 使用TableSchemaResolver的getTableInternalSchemaFromCommitMetadata方法獲取最新的完整InternalSchema
  2. 使用HudiParquetPageSources類的createParquetPageSource方法傳入參數regularColumns(List),與完整InternalSchema通過InternalSchemaUtils.pruneInternalSchema方法獲取剪枝後的InternalSchema
  3. 通過FSUtils.getCommitTime方法利用文件名的時間戳獲取commitInstantTime,再利用InternalSchemaCache.getInternalSchemaByVersionId方法獲取文件的InternalSchema
  4. 使用InternalSchemaMerger的mergeSchema方法,獲取剪枝後的查詢InternalSchema和文件InternalSchema進行merge的InternalSchema
  5. 使用merge後的InternalSchema的列名list,轉換爲HudiParquetPageSources的requestedSchema,改變HudiParquetPageSources的getDescriptors和getColumnIO等方法邏輯的結果

實現爲 https://github.com/prestodb/presto/pull/18557 (打開狀態)

快照的改造

基本思想:改造huid-hadoop-mr模塊的InputFormat,獲取數據和查詢的 InternalSchema ,將merge後的schema列信息設置爲hive任務所需的屬性,進行查詢。

具體步驟:

1.基礎文件支持完整schema演變,spark-sql的實現此處無法複用,添加轉換類,在HoodieParquetInputFormat中使用轉換類,根據commit獲取文件schema,根據查詢schema和文件schema進行merge,將列名和屬性設置到job的屬性裏serdeConstants.LIST_COLUMNS,ColumnProjectionUtils.READ_COLUMN_NAMES_CONF_STR,serdeConstants.LIST_COLUMN_TYPES;

2.日誌文件支持完整schema演變,spark-sql的實現此處可以複用。HoodieParquetRealtimeInputFormat的RealtimeCompactedRecordReader中,使用轉換類設置reader對象的幾個schema屬性,使其複用現有的merge數據schema與查詢schema的邏輯。

已經存在pr可以達到目標 https://github.com/apache/hudi/pull/6989 (合入master,0.13)

Presto的配置

${presto_home}/etc/catalog/hudi.properties,基本複製hive.properties;主要修改爲

connector.name=hudi

Presto的部署

此處分別爲基於hudi0.12.1和prestodb的release0.275合入pr後打的包,改動涉及文件不同版本間差異不大,無需關注版本問題

分別將mor表改造涉及的包:

hudi-presto-bundle-0.12.1.jar

以及cow表改造涉及的包:

presto-hudi-0.275.1-SNAPSHOT.jar

放入${presto_home}/etc/catalog/hudi.propertiesplugin/hudi

重啓presto服務

開發過程遇到的問題及解決

總結

當前已經實現PrestoDB對Hudi的快照讀,以及對schema完整演變的支持,滿足了大批量表以MOR的表格式快速寫入數據湖,且頻繁變更表結構的同時,能夠準確實時地進行OLAP分析的功能。但由於Trino社區更加活躍,以前的很多功能基於Trino開發,下一步計劃改造Trino,使其完整支持快照讀與兩種查詢模式下的schema完整演變。

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