一、資料湖的角色和定位
随着移動網際網路,物聯網技術的發展,資料的應用逐漸從 BI 報表可視化往機器學習、預測分析等方向發展,即 BI 到 AI 的轉變。
資料的使用者也從傳統的業務分析人員轉為資料科學家,算法工程師。
此外對資料的實時性要求越來越高,也出現了越來越多的非結構化的資料。
目前的資料倉庫技術出現了一定的局限性,比如單一不變的 schema 和模型已經無法滿足各類不同場景和領域的資料分析的要求,并且資料科學家更願意自己去處理原始的資料,而不是直接使用被處理過的資料。
比如對于資料缺失這種情況,資料科學家會嘗試各種不同的算法去彌補缺失資料,針對不同的業務場景也會有不同的處理方式。
目前資料湖相關的技術是業界針對這些問題的一種解決方案。
下表展示了資料倉庫和資料湖在各個次元上的特性:
相比于資料倉庫,資料湖會保留最原始的資料,并且是讀取時确定 Schema,這樣可以在業務發生變化時能靈活調整。最原始的資料湖技術其實就是對象存儲,比如 Amazon S3,Aliyun OSS,可以存儲任意形式的原始資料,但是如果不對這些存儲的原始檔案加以管理,就會使資料湖退化成資料沼澤(dataswamp)。
是以必須有相關的技術發展來解決這些問題。
我們都知道一個大資料處理系統分為:
- 分布式檔案系統:HDFS,S3
- 基于一定的檔案格式将檔案存儲在分布式檔案系統:Parquet,ORC, ARVO
- 用來組織檔案的中繼資料系統:Metastore
- 處理檔案的計算引擎,包括流處理和批處理:SPARK,FLINK
簡單的說,資料湖技術是計算引擎和底層存儲格式之間的一種資料組織格式,用來定義資料、中繼資料的組織方式。
并實作以下的一些功能:
- 支援事務 (ACID)
- 支援流批一體
- 支援 schema 演化和 schema 限制
- 支援多種底層資料存儲HDFS,OSS,S3,目前并沒有針對資料湖的比較成熟的解決方案,幾個大廠在開發相關技術來解決内部遇到的一些痛點後,開源了幾個項目,比較著名的有Databrics 的 Dalta Lake,Uber 開源的 Hudi,Netflix 開源的 Iceberg。
二、Delta Lake
傳統的 lambda 架構需要同時維護批處理和流處理兩套系統,資源消耗大,維護複雜。
基于 Hive 的數倉或者傳統的檔案存儲格式(比如 parquet / ORC),都存在一些難以解決的問題:
- 小檔案問題;
- 并發讀寫問題;
- 有限的更新支援;
- 海量中繼資料(例如分區)導緻 metastore 不堪重負
如上圖,Delta Lake 是 Spark 計算架構和存儲系統之間帶有 Schema 資訊的存儲中間層。
它有一些重要的特性:
- 設計了基于 HDFS 存儲的中繼資料系統,解決 metastore 不堪重負的問題;
- 支援更多種類的更新模式,比如 Merge / Update / Delete 等操作,配合流式寫入或者讀取的支援,讓實時資料湖變得水到渠成;
- 流批操作可以共享同一張表;
- 版本概念,可以随時回溯,避免一次誤操作或者代碼邏輯而無法恢複的災難性後果。
Delta Lake 是基于 Parquet 的存儲層,所有的資料都是使用 Parquet 來存儲,能夠利用 parquet 原生高效的壓縮和編碼方案。
Delta Lake 在多并發寫入之間提供 ACID 事務保證。每次寫入都是一個事務,并且在事務日志中記錄了寫入的序列順序。
事務日志跟蹤檔案級别的寫入并使用樂觀并發控制,這非常适合資料湖,因為多次寫入/修改相同的檔案很少發生。在存在沖突的情況下,Delta Lake 會抛出并發修改異常以便使用者能夠處理它們并重試其作業。
Delta Lake 其實隻是一個 Lib 庫,不是一個 service,不需要單獨部署,而是直接依附于計算引擎的,但目前隻支援 spark 引擎,使用過程中和 parquet 唯一的差別是把 format parquet 換成 delta 即可,可謂是部署和使用成本極低。
三、Apache Hudi
1. Hudi 是什麼
一般來說,我們會将大量資料存儲到HDFS/S3,新資料增量寫入,而舊資料鮮有改動,特别是在經過資料清洗,放入資料倉庫的場景。
且在資料倉庫如 hive中,對于update的支援非常有限,計算昂貴。
另一方面,若是有僅對某段時間内新增資料進行分析的場景,則hive、presto、hbase等也未提供原生方式,而是需要根據時間戳進行過濾分析。
Apache Hudi 代表 Hadoop Upserts anD Incrementals,能夠使HDFS資料集在分鐘級的時延内支援變更,也支援下遊系統對這個資料集的增量處理。
Hudi資料集通過自定義的 nputFormat 相容目前 Hadoop 生态系統,包括 Apache Hive,Apache Parquet,Presto 和 Apache Spark,使得終端使用者可以無縫的對接。
如下圖,基于 Hudi 簡化的服務架構,分鐘級延遲。
2. Hudi 存儲的架構
如上圖,最下面有一個時間軸,這是 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 的架構更加的優雅,對于資料格式、類型系統有完備的定義和可進化的設計。
但是 Iceberg 缺少行級更新、删除能力,這兩大能力是現有資料組織最大的賣點,社群仍然在優化中。
五、總結
下表從各個次元,總結了三大資料湖架構支援的特性。
如果用一個比喻來說明delta、iceberg、hudi、三者差異的話,可以把三個項目比做建房子。
- Delta的房子底座相對結實,功能樓層也建得相對比較高,但這個房子其實可以說是databricks的,本質上是為了更好地壯大Spark生态,在delta上其他的計算引擎難以替換Spark的位置,尤其是寫入路徑層面。
- Iceberg的建築基礎非常紮實,擴充到新的計算引擎或者檔案系統都非常的友善,但是現在功能樓層相對低一點,目前最缺的功能就是upsert和compaction兩個,Iceberg社群正在以最高優先級推動這兩個功能的實作。
- Hudi的情況要相對不一樣,它的建築基礎設計不如iceberg結實,舉個例子,如果要接入Flink作為Sink的話,需要把整個房子從底向上翻一遍,把接口抽象出來,同時還要考慮不影響其他功能,當然Hudi的功能樓層還是比較完善的,提供的upsert和compaction功能直接命中廣大群衆的痛點。