Hudi 1.0 新功能預覽
一、Apache Hudi 簡介
Hudi 是一個高效的事務型數(shù)據(jù)湖倉平臺,其核心特色是一個開放性的表格式定義和一套全面的事務數(shù)據(jù)庫核心層。這一核心層不僅支持索引功能,還能高效地處理并發(fā)事務,并具備強大的變更數(shù)據(jù)捕獲能力。在數(shù)據(jù)管道中,Hudi 能夠從上游數(shù)據(jù)源如 Kafka 接收數(shù)據(jù),并利用 Spark 和 Flink 等執(zhí)行引擎進行數(shù)據(jù)導入與處理。平臺還提供自動文件大小調整、增量處理和變更捕獲等功能,以優(yōu)化數(shù)據(jù)的清理與轉換。此外,Hudi 具備豐富的表服務能力,如數(shù)據(jù)清理和聚類,確保數(shù)據(jù)管理的高效性。Hudi 已與眾多數(shù)據(jù)生態(tài)系統(tǒng)組件廣泛集成,包括目錄同步和多種查詢引擎,以實現(xiàn)數(shù)據(jù)處理的多樣化和靈活性。
上圖是 Hudi 的架構分層圖,展示了其作為數(shù)據(jù)湖倉平臺的三個核心層級。最底層用綠色表示的是開放的存儲層,負責底層數(shù)據(jù)的存儲,使用諸如 Parquet、Avro 和 ORC 等開源文件格式。其上一層是事務性數(shù)據(jù)庫核心層,該層定義了數(shù)據(jù)表格式、數(shù)據(jù)表服務、索引機制以及并發(fā)控制,確保了數(shù)據(jù)的一致性和高效處理。最頂層是開放平臺服務層,提供了一套開發(fā)的 API,支持與各類集成生態(tài)技術的讀寫操作,并實現(xiàn)了平臺的功能性,包括元數(shù)據(jù)服務、數(shù)據(jù)入湖工具和與不同查詢引擎的集成。這些層級共同構成了 Hudi 的平臺架構,使得用戶和開發(fā)人員能夠直接與平臺服務及工具交互,而機器則通過底層的讀寫支持與數(shù)據(jù)庫核心層交互。在 Hudi 1.0 版本中,這些架構層級經(jīng)過了新的性能和功能優(yōu)化,以滿足現(xiàn)代數(shù)據(jù)管理的需求。
二、Hudi 1.0 的重新思考
Hudi 1.0 的構想源于對 Hudi 成長歷程的回顧。Hudi 最初旨在解決大規(guī)模數(shù)據(jù)攝取、增量數(shù)據(jù)處理和快速創(chuàng)建的問題,并需要與 Presto、Spark、Flink、Trino 等查詢引擎集成。雖然這些引擎在列式數(shù)據(jù)查詢方面表現(xiàn)出色,但它們的整合過程卻充滿挑戰(zhàn)。
作為湖倉領域的先鋒,Hudi 曾被稱為事務性數(shù)據(jù)湖。在生態(tài)系統(tǒng)的早期階段,Hudi 做出了一些保守的設計選擇,例如為不同的查詢引擎開發(fā)了各自的連接器。在過去的五年中,Hudi 社區(qū)蓬勃發(fā)展,同時也暴露出更多元化的需求,特別是在事務性操作、快速更新和刪除功能方面。因此,Hudi 團隊認識到有必要對現(xiàn)有設計進行深層次的重新思考和優(yōu)化,以適應不斷變化的數(shù)據(jù)處理需求。
Hudi 1.0 的五大發(fā)展方向體現(xiàn)了對當前數(shù)據(jù)湖架構的深度思考和對未來規(guī)劃的遠見:
- 深度查詢引擎集成:Hudi 將探索更深層次的查詢引擎集成,減少對多個專屬連接器的依賴。通過深入集成 Hudi 的多模索引(metadata table)功能,并結合如 Velox 等組件,以提升查詢性能。
- 泛化的關系型數(shù)據(jù)模型:隨著越來越多的引擎支持更全面的 SQL 語法,Hudi 將擴展其數(shù)據(jù)模型,以更好地適應關系型數(shù)據(jù)處理的需求,充分利用現(xiàn)代數(shù)據(jù)生態(tài)系統(tǒng)的優(yōu)勢。
- 有服務器和無服務器的架構配置:Hudi 將發(fā)展混合架構,結合有服務器和無服務器配置,以適應不同的數(shù)據(jù)處理需求。元數(shù)據(jù)服務將得到加強,以支持更高效的并發(fā)控制和商用元數(shù)據(jù)服務的集成。
- 非結構化數(shù)據(jù)支持:Hudi 將擴展對非結構化數(shù)據(jù)(如圖像、視頻)的支持,以避免數(shù)據(jù)湖的碎片化,并實現(xiàn)非結構化數(shù)據(jù)的索引更新和變更捕獲功能,以適應 AI 等新興技術的需求。
- 數(shù)據(jù)庫表的自我管理能力提升:Hudi 將繼續(xù)完善其數(shù)據(jù)湖表管理服務,包括數(shù)據(jù)入湖、表服務計劃執(zhí)行和運維工具。未來將增加更多功能,如反向流數(shù)據(jù)導入、快照管理、檢驗分析報告工具、跨區(qū)域備份和記錄級別的 TTL 管理,以實現(xiàn)更全面的數(shù)據(jù)表管理。
在對比數(shù)據(jù)庫的經(jīng)典設計架構與 Hudi 的架構時,我們可以看到 Hudi 在數(shù)據(jù)湖倉領域實現(xiàn)了一個類似數(shù)據(jù)庫的功能層次。最頂層的客戶端模塊是用戶與系統(tǒng)交互的接口,負責處理 SQL 層的請求。在此之下,Hudi 集成了不同查詢引擎的查詢解析和優(yōu)化功能,目前主要依賴各引擎自身的優(yōu)化,但未來可能引入 Hudi 內部的優(yōu)化機制。
藍色的模塊代表與外部系統(tǒng)的集成,而中間的方塊則展示了 Hudi 實現(xiàn)的事務性管理機制,這與傳統(tǒng)數(shù)據(jù)庫中的鎖管理、日志管理等模塊相對應。已經(jīng)實現(xiàn)的組件用綠色方塊表示,而像緩存管理這樣的未來愿景則用其他顏色表示,這表明了 Hudi 1.0 版本的發(fā)展方向。
三、Hudi 1.0-beta 的重點新功能
接下來,將介紹 Hudi 1.0 的一些關鍵功能。Hudi 1.0 版本的設計理念可以在其RFC(Request for Comments)文檔中找到,該文檔詳細介紹了版本的主要設計方向和預期特性。
接下來將重點討論幾個關鍵功能,首先是 Hudi 的 LSM tree 時間線。Hudi 的時間線本質上是一個不可變的事務日志,記錄了表上所有已完成交易的詳細信息。這個事務日志通常會隨著每次提交而線性增長。Hudi 0.x 版本維護了兩個時間線:活躍時間線和歸檔時間線?;钴S時間線用于快速檢索最新信息,而歸檔時間線則用于滿足特定場景下的歷史數(shù)據(jù)查詢需求。由于歸檔數(shù)據(jù)采用了不同的存儲機制,訪問這些數(shù)據(jù)的成本相對較高。
為了優(yōu)化 Hudi 時間線的存儲效率,我們考慮采用 LSM tree 這種經(jīng)過驗證的高效數(shù)據(jù)結構,它特別適合處理大規(guī)模寫入操作。在 Hudi 1.0 中,我們重新實現(xiàn)了寫入時間線的機制,現(xiàn)在每次寫入都會記錄其起始和結束時間。此外,我們將現(xiàn)有的線性存儲結構轉換成了 LSM tree 結構,這允許進行更高效的壓縮操作。
LSM tree 的優(yōu)勢在于其樹形分層結構,能夠在頂層(如內存或緩存)快速檢索信息,同時在更長的時間線上,可以通過壓縮多個事務到更大的 parquet 文件中,來提高讀取效率和存儲效率。這種結構不僅優(yōu)化了數(shù)據(jù)的快速訪問,還提升了整體存儲的緊湊性和性能。
接下來對 LSM Tree 時間線進行了一系列測試,其中包括模擬了 100 萬次提交的交易日志。在無需加載所有元數(shù)據(jù)的情況下,僅訪問開始時間和結束時間以及事務中涉及的文件,我們實現(xiàn)了在 367ms 內加載整個時間線。這一性能提升得益于將時間線數(shù)據(jù)存儲為 parquet 文件,這不僅減少了讀取所需的元數(shù)據(jù)量,還提供了更高的讀取靈活性,從而顯著提高了加載效率。
接下來探討另一個關鍵功能:函數(shù)索引。Hudi 的當前版本已經(jīng)集成了一個多模式索引子系統(tǒng),支持文件索引、列統(tǒng)計和布隆過濾器等功能,且這些索引可以異步構建。在 Hudi 1.0 中,我們希望進一步增強多模式索引的通用性。受到數(shù)據(jù)庫索引機制的啟發(fā),我們考慮了基于 R 樹的空間索引和基于 Lucene 的搜索索引等高級功能。例如,PostgreSQL 能夠在表達式上創(chuàng)建索引,這啟發(fā)了我們實現(xiàn)函數(shù)索引的思路。函數(shù)索引的引入將使 Hudi 的索引功能更加靈活和強大,從而提高查詢效率和處理復雜查詢的能力。
函數(shù)索引的一個典型用例是在處理包含組織 ID 和時間戳的事件流數(shù)據(jù)時。通常,我們希望根據(jù)組織 ID 進行分區(qū),然后進一步根據(jù)時間戳細分。例如,如果有1,000 個組織的一整年數(shù)據(jù),我們可能會創(chuàng)建 365,000 個分區(qū)。然而,這種做法可能會導致數(shù)據(jù)傾斜和大量小文件的問題,這既影響了存儲效率,也降低了查詢性能。
為了解決這個問題,Hudi 1.0 引入了函數(shù)索引。我們僅根據(jù)組織 ID 進行物理分區(qū),然后為時間戳定義一個函數(shù),比如將 Unix 時間戳轉換為小時,并記錄每個小時的最大值和最小值。這樣就可以在索引中實現(xiàn)高效的 data skipping,即跳過不需要掃描的數(shù)據(jù),同時保持文件存儲的高效率,無需進行更細粒度的物理分區(qū)。這種方法既保留了存儲效率,又提供了更靈活的分區(qū)結構,優(yōu)化了查詢性能和數(shù)據(jù)管理。
函數(shù)索引的使用可以通過一個簡單的示例來解釋。在左側的 SQL 示例中,使用 city 來分區(qū),同時還有一個時間戳字段。我們不需要針對時間戳進一步分區(qū),而是可以使用 create index 的語法來生成一個新的索引,該索引將時間戳轉換為小時。一旦生成了這個索引,隨后的 SELECT 語句就可以利用時間戳進行高效的數(shù)據(jù)跳過。
在右側的兩個 Spark DAG(有向無環(huán)圖)演示中,展示了在有函數(shù)索引和沒有函數(shù)索引的情況下,數(shù)據(jù)跳過是如何實現(xiàn)的。使用函數(shù)索引,Spark 查詢可以更有效地跳過不相關數(shù)據(jù),從而提高查詢性能和減少資源消耗。這種索引策略不僅簡化了數(shù)據(jù)分區(qū),還提升了整體的數(shù)據(jù)處理效率。
另一個重要的功能改進是 Hudi 新開發(fā)的文件組讀取器和寫入器。Hudi 自創(chuàng)建之初就設計了一個基于主鍵的概念。在 MOR(Merge-On-Read)表類型中,我們實現(xiàn)了一個合并操作,從第一天起就支持快照查詢,即實時地將日志數(shù)據(jù)合并到基礎文件中。這一機制確保了即使在數(shù)據(jù)不斷寫入和更新時,也能高效地執(zhí)行查詢操作,提供了對歷史和最新數(shù)據(jù)的統(tǒng)一視圖。
對于 Hudi 中的合并操作,我們發(fā)現(xiàn)了潛在的優(yōu)化空間。具體來說,我們可以在記錄日志的同時,記錄下日志所需更新的基礎文件的位置信息。這樣,在進行合并操作時,能夠直接定位到文件的具體位置,從而高效地執(zhí)行合并。
此外,Hudi 1.0 增加了對部分更新(partial update)的優(yōu)先支持。傳統(tǒng)的日志文件默認記錄整條更新語句,但在許多情況下,只需記錄更新的字段。通過僅記錄更新的字段及其值和位置,能夠極大優(yōu)化合并過程。
設計文件組讀取器和寫入器的另一個好處是,它使得與各種查詢引擎的集成變得更加簡便。這種設計統(tǒng)一了接口,使得擴展對不同引擎的支持變得更加方便,從而提升了 Hudi 的整體靈活性和可擴展性。
針對基于位置的合并操作,我們進行了一系列基準測試。這些測試涉及了兩個不同規(guī)模的合并表,一個包含 500GB、7.5 億條記錄,另一個包含 1TB、15 億條記錄。每條記錄大約 1KB 大小,表包含 1000 個分區(qū),每個文件大約 256MB。
在測試中,我們首先批量加載數(shù)據(jù),然后執(zhí)行刪除和更新操作,更新表中 50% 的記錄。通過使用文件組讀取器并利用位置信息進行合并操作,我們觀察到了 12% 到 20% 的性能提升。這一提升隨著數(shù)據(jù)量的增加而變得更加顯著。對于具體的性能數(shù)據(jù)和詳細分析,可以參考 Hudi 的 PR 10167。
在部分更新(partial update)的測試中,我們觀察到了更為顯著的性能提升。上圖中展示了更新操作的結果對比。當使用全量更新,即整條記錄的所有字段與僅更新部分字段時,更新的延遲降低了 1.4 倍。同時,寫入的文件大小減少了 70 倍,這是因為我們節(jié)省了大量未更新的數(shù)據(jù)。由于寫入更為高效,節(jié)省了空間,合并操作也變得更為高效,我們觀察到了 5.7 倍的性能提升。這些優(yōu)化不僅提高了更新操作的效率,還顯著減少了存儲空間的占用。
最后一個重點功能是非阻塞并發(fā)控制。這個設計基于一個常見場景:一個每分鐘寫入的進程和每小時執(zhí)行一次的 GDPR 刪除作業(yè)。如果采用樂觀鎖機制,刪除作業(yè)可能會頻繁遇到?jīng)_突,因為刪除操作是隨機的,這會導致刪除作業(yè)在大多數(shù)情況下都需要重試,從而浪費資源。
Hudi 從一開始就采用了 MVCC(多版本并發(fā)控制)機制。在寫入側,允許不加阻塞地寫入,而在使用 MOR 模式時,在合并側執(zhí)行異步合并操作。此外,我們可以利用合并的機會進行類聚操作,以優(yōu)化存儲。這種設計確保了在處理并發(fā)寫入和刪除操作時,系統(tǒng)的效率和資源利用率得到提高。
為了解決多個寫入器并發(fā)控制的問題,Hudi 支持樂觀鎖的使用,也引入了早期沖突檢測機制。此外,Hudi 探索了更通用的非阻塞多版本并發(fā)控制機制。在 Hudi 1.0 中,我們實現(xiàn)了一個基于 MOR 寫入過程的非阻塞并發(fā)控制。
當 MOR 寫入操作在不同寫入器上生成不同的日志文件時,Hudi 最初不會阻塞寫入過程。通過使用全局單調遞增的時間戳來記錄每個寫入的開始和結束時間,我們可以在合并或快照讀取階段解決潛在的沖突。這種方法允許在寫入時保持非阻塞狀態(tài),從而提高了寫入效率,同時確保了數(shù)據(jù)的最終一致性。
今天的重點內容已經(jīng)介紹完畢。關于 Hudi 1.0,這里再提供一些額外信息。Hudi 1.0 的技術文檔已經(jīng)發(fā)表,可以在相應的鏈接中查閱。此外,相關的RFC 文檔和設計文檔也已公開,供社區(qū)參考。Hudi 1.0 beta1 的 jar 包也已經(jīng)發(fā)布。這些資源將為希望深入了解 Hudi 1.0 的用戶和開發(fā)者提供幫助。
最后,展示一下 Hudi 社區(qū)的活躍情況。社區(qū)非常歡迎新成員的加入,這里提供了豐富的文檔鏈接和資源,以便大家更好地了解和參與 Hudi 項目。此外,歡迎關注 Hudi 的公眾號來獲取最新信息,也歡迎加入我們的社區(qū),共同推動 Hudi 的發(fā)展。
四、相關鏈接
1.0技術spec:https://hudi.apache.org/tech-specs-1 point0。
Docs : https://hudi.apache.org。
Blogs : https://hudi.apache.org/blog。
slack:https://join.slack.com/t/apache-hudi/shared_invite/zt-2ggm1fub8-_yt4Reu9djwqqVRFC7X49g。
Twitter : https://twitter.com/apachehudi。
Github: https://github.com/apache/hudi/。
Mailing list(s) : dev-subscribe@hudi.apache.org (send an empty email to subscribe)。