資料湖三劍客,大資料時代的新正規化?
隨著網際網路高速發展,大資料技術快速發展和迅速迭代,降低了使用者處理海量資料的門檻,越來越多的應運場景出現在我們的身邊儲存和處理需求越來越多樣化,逐漸呈現出資料倉庫往資料湖方向發展、批處理往流式處理髮展、本地部署往雲模式發展的趨勢。
但在技術發展層面,逐漸出現了諸多的掣肘,不斷有新的問題出現,僅僅就儲存方面來講,與資料庫這樣高度優化的技術相比,大資料技術的抽象和實現還是太原始和初級。
目前的資料倉庫技術出現了一定的侷限性,比如單一不變的 schema 和模型已經無法滿足各類不同場景和領域的資料分析的要求、流批一體的資料儲存引擎和計算引擎適配問題以及Hadoop體系檔案系統的ACID能力缺失等問題急需要解決,但就目前的技術棧而言,打破這些技術的桎梏,似乎多少有些力不從心,這個過程中,一項重大變革似乎在破曉前顯得尤為必要。
在不停地探索和思考中,大資料人開始慢慢醒悟,回頭看向成名已久的資料庫,將更多資料庫的成熟技術和理念借鑑到大資料中,似乎是一條高效又穩健的道路。至此,資料湖技術應運而生,在諸多方面向資料庫看齊,你可以說是學習,當然,也可以說是致敬,是業界針對這些問題的一種解決方案。
那麼,什麼是資料湖技術呢?
計算引擎之下、資料儲存之上,處於中間層的資料湖。
簡單地說,這類新技術是介於上層計算引擎和底層儲存格式之間的一箇中間層,我們可以把它定義成一種“資料組織格式”。其最核心的點便是將事務能力帶到了大資料領域,並抽象成統一的中間格式供不同引擎適配對接。
為此,Uber開源了Apache Hudi,Databricks提出了Delta Lake,而 Netflix 則發起了 Apache Iceberg 專案,一時間這種具備 ACID 能力的表格式中介軟體成為了大資料、資料湖領域炙手可熱的方向。
Iceberg 將其稱之為“表格式”也是表達類似的含義。它與底層的儲存格式(比如 ORC、Parquet 之類的列式儲存格式)最大的區別是,它並不定義資料儲存方式,而是定義了資料、元資料的組織方式,向上提供統一的“表”的語義。它構建在資料儲存格式之上,其底層的資料儲存仍然使用 Parquet、ORC 等進行儲存。
Apache Iceberg、Hudi 和 Delta Lake 誕生於不同公司,需要解決的問題存在差異,因此三者在設計初衷上稍有不同。
其中,Iceberg 的設計初衷更傾向於定義一個標準、開放且通用的資料組織格式,同時遮蔽底層資料儲存格式上的差異,向上提供統一的操作 API,使得不同的引擎可以通過其提供的 API 接入;Hudi 的設計初衷更像是為了解決流式資料的快速落地,並能夠通過 upsert 語義進行延遲資料修正;Delta Lake 作為 Databricks 開源的專案,更側重於在Spark 層面上解決 Parquet、ORC 等儲存格式的固有問題,並帶來更多的能力提升。
雖然這三個專案在設計初衷上稍有不同,但實現的思路和提供的能力卻非常相似,他們都提供了 ACID 的能力,都基於樂觀鎖實現了衝突解決和提供線性一致性,同時相應地提供了 time travel 的功能。
但是因為設計初衷的不同,三個專案當前的能力象限各有不同,Iceberg 在其格式定義和核心能力上最為完善,但是上游引擎的適配上稍顯不足;Hudi 基於 Spark 打造了完整的流式資料落地方案,但是其核心抽象較弱,與 Spark 耦合較緊;Delta Lake 同樣高度依賴於 Spark 生態圈,與其他引擎的適配尚需時日。
那麼,當下資料湖技術呈現的三足鼎立的技術場面,優劣點如何看待,技術選型如何去做,當然是我們最關心的問題,下文逐步解析。
Delta Lake,spark生態圈急先鋒
傳統的 lambda 架構需要同時維護批處理和流處理兩套系統,資源消耗大,維護複雜。
基於 Hive的數倉或者傳統的檔案儲存格式(比如 parquet / ORC),都存在一些難以解決的問題:小檔案問題、併發讀寫問題、有限的更新支援及海量元資料(例如分割槽)導致 metastore不堪重負問題等。
如上圖,Delta Lake 是 Spark 計算框架和儲存系統之間帶有 Schema 資訊的儲存中間層。
它集中解決了傳統hive數倉的諸多問題,使得實時資料湖變得優雅又絲滑,不見了天生的慵懶,只看到輕盈又婀娜的身姿。重要變化如下:
- 設計了基於 HDFS 儲存的元資料系統,解決 metastore 不堪重負的問題;
- 支援更多種類的更新模式,比如 Merge / Update / Delete 等操作,配合流式寫入或者讀取的支援,讓實時資料湖變得水到渠成;
- 流批操作可以共享同一張表;
- 版本概念,可以隨時回溯,避免因為一次誤操作或者程式碼邏輯而無法恢復的災難性後果。
基於Parquet的列式儲存層,在多併發寫入之間提供 ACID 事務保證。每次寫入都是一個事務,並且在事務日誌中記錄了寫入的序列順序。
但是,Delta Lake定位於spark流批一體的資料處理工具,地主家的公子,自己家的事情如數家珍,輕鬆搞定,但走出家門後,難免會有些水土不服。
Apache Hudi,有天生缺陷的優等生
Apache Hudi 代表 Hadoop Upserts and Incrementals,能夠使HDFS資料集在分鐘級的時延內支援變更,也支援下游系統對這個資料集的增量處理。
Hudi資料集通過自定義的inputFormat 相容當前 Hadoop 生態系統,包括 Apache Hive,Apache Parquet,Presto 和 Apache Spark,使得終端使用者可以無縫的對接。
如下圖,基於 Hudi 簡化的服務架構,分鐘級延遲。
Hudi 會維護一個時間軸,在每次執行操作時(如寫入、刪除、合併等),均會帶有一個時間戳。
通過時間軸,可以實現在僅查詢某個時間點之後成功提交的資料,或是僅查詢某個時間點之前的資料。
這樣可以避免掃描更大的時間範圍,並非常高效地只消費更改過的檔案(例如在某個時間點提交了更改操作後,僅 query 某個時間點之前的資料,則仍可以 query 修改前的資料)。
如上圖的左邊,Hudi 將資料集組織到與 Hive 表非常相似的基本路徑下的目錄結構中。
資料集分為多個分割槽,每個分割槽均由相對於基本路徑的分割槽路徑唯一標識。
如上圖的中間部分,Hudi 以兩種不同的儲存格式儲存所有攝取的資料。
讀優化的列存格式(ROFormat):僅使用列式檔案(parquet)儲存資料。在寫入/更新資料時,直接同步合併原檔案,生成新版本的基檔案(需要重寫整個列資料檔案,即使只有一個位元組的新資料被提交)。此儲存型別下,寫入資料非常昂貴,而讀取的成本沒有增加,所以適合頻繁讀的工作負載,因為資料集的最新版本在列式檔案中始終可用,以進行高效的查詢。
寫優化的行存格式(WOFormat):使用列式(parquet)與行式(avro)檔案組合,進行資料儲存。在更新記錄時,更新到增量檔案中(avro),然後進行非同步(或同步)的compaction,建立列式檔案(parquet)的新版本。此儲存型別適合頻繁寫的工作負載,因為新記錄是以appending 的模式寫入增量檔案中。但是在讀取資料集時,需要將增量檔案與舊檔案進行合併,生成列式檔案。
Apache Iceberg,基礎紮實,後生可畏
Iceberg 作為新興的資料湖框架之一,開創性地抽象出“表格式”table format)這一中間層,既獨立於上層的計算引擎(如Spark和Flink)和查詢引擎(如Hive和Presto),也和下層的檔案格式(如Parquet,ORC和Avro)相互解耦。
此外 Iceberg 還提供了許多額外的能力:
-
ACID事務;
-
時間旅行(time travel),以訪問之前版本的資料;
-
完備的自定義型別、分割槽方式和操作的抽象;
-
列和分割槽方式可以進化,而且進化對使用者無感,即無需重新組織或變更資料檔案;
-
隱式分割槽,使SQL不用針對分割槽方式特殊優化;
-
面向雲端儲存的優化等;
Iceberg的架構和實現並未綁定於某一特定引擎,它實現了通用的資料組織格式,利用此格式可以方便地與不同引擎(如Flink、Hive、Spark)對接。
所以 Iceberg 的架構更加的優雅,對於資料格式、型別系統有完備的定義和可進化的設計。
綜合而言,三個引擎的初衷場景並不完全相同,Hudi 為了 incremental 的 upserts,相對而言最為成熟,但底層架構設計較差,擴充套件性及生態延續方面難度較大;Iceberg 定位於高效能的分析與可靠的資料管理,底層架構的抽象及架構的開放性方面做的很好,資料湖upsert和compaction兩個關鍵的功能也趨於完善,正在快速發展期;Delta 定位於流批一體的資料處理,無縫對接Spark生態。
我們在技術選型的時候,不僅要知到要到哪裡去,更要明確我們從哪裡來,選擇適合自己當下業務需求的技術,才能更快速、更高效輔助業務開發。