Kudu、Hudi和Delta Lake的比較
kudu、hudi和delta lake是目前比較熱門的支持行級別數據增刪改查的存儲方案,本文對三者之間進行了比較。
存儲機制
kudu
kudu的存儲機制和hudi的寫優化方式有些相似。
kudu的最新數據保存在內存,稱為MemRowSet(行式存儲,基於primary key有序),
當MemRowSet寫滿(默認1G或者120s)后flush到磁盤,形成DiskRowSet(列式存儲)。
DiskRowSet包含baseData與DeltaStores兩部分,DeltaStores包含一個DeltMemStore和多個DeltaFile,
后續的更新數據存放在DeltMemStore中,增長到一定程度后flush成DeltaFile文件。
kudu會定期執行compaction操作,將DeltaFile中的更新合並到DiskRowSet,或者合並DiskRowSet,清除已刪除的數據,並減少DiskRowSet的數量。
hudi
hudi維護了一個時間軸,記錄了在不同時刻對數據集進行的所有操作。
hudi擁有2種存儲優化,讀優化適合讀多寫少的場景,寫優化適合讀少寫多的場景。
- 讀優化(Copy On Write):在每次commit后都將最新的數據compaction成列式存儲(parquet);
- 寫優化(Merge On Read):對增量數據使用行式存儲(avro),后台定期將它compaction成列式存儲。
delta lake
delta lake的存儲機制和hudi的讀優化方式相似。
delta lake的數據不會保存在內存中,而是直接寫到新的數據文件中(parquet格式),同時在commit log中添加AddFile這種FileAction,快照新建/更新時讀取事務日志,會加載新的數據文件信息。
讀數據
kudu
先根據要掃描數據的主鍵范圍,定位到目標的tablets,然后讀取tablets中的DiskRowSet。
在讀取每個DiskRowSet時,先根據主鍵過濾要scan的范圍,然后加載范圍內的baseData,再找到對應的DeltaStores,
應用所有變更,最后union上MemRowSet中的內容,最后返回數據給client。
kudu提供range分區和hash分區兩種分區方式,通過多種索引(主鍵范圍索引、bloomfilter、主鍵索引),支持隨機讀取數據和高效的批量讀取數據。
hudi
hudi也維護着一個索引,以此將key快速映射到對應的fileId。索引的實現是插件式的,默認是bloomFilter,也可以使用HBase。
hudi提供3種查詢視圖。
- 讀優化視圖:僅提供compaction后的列式存儲的數據;
- 增量視圖:僅提供一次compaction/commit前的增量數據;
- 實時視圖:包括列式存儲數據和寫優化的行式存儲數據。
delta lake
通過讀取事務日志的checkpoint文件(parquet格式)和之后版本的commit文件(json格式),
建立當前最新的快照,該快照包含了當前版本所有數據文件的地址,然后使用spark讀取數據。
更新數據
kudu
client向master發出請求,通過索引定位到具體的tablet,然后根據元數據連接tablet對應的tserver。
若數據在磁盤(DiskRowSet)上,則將更新信息寫入DeltMemStore中,若數據在內存(MemRowSet)中,則將信息寫入所在行的mutation鏈表中。
hudi
hudi沒有傳統意義的更新,只有append和重寫。
hudi寫數據的時候需要指定以下3個key。
- RECORDKEY_FIELD_OPT_KEY:每條記錄的唯一id,支持多個字段;
- PRECOMBINE_FIELD_OPT_KEY:在數據合並的時候使用到,當RECORDKEY_FIELD_OPT_KEY相同時,默認取PRECOMBINE_FIELD_OPT_KEY屬性配置的字段最大值所對應的行;
- PARTITIONPATH_FIELD_OPT_KEY:用於存放數據的分區字段。
hudi更新數據和插入數據很相似(寫法幾乎一樣),更新數據時,會根據以上三個字段對數據進行Merge。
delta lake
delta lake更新數據時會先定位待更新數據所在的文件,使用spark join獲得結果數據集,將更新后的數據和文件中其他不需要更新的數據一起寫入到新的文件里,同時在commit log中記錄AddFile(新文件)和RemoveFile(舊文件)兩種action。
其他
| --- | kudu | hudi | delta lake |
|---|---|---|---|
| 行級別更新 | 支持 | 支持 | 支持 |
| schema修改 | 支持 | 支持 | 支持 |
| 批流共享 | 支持 | 支持 | 支持 |
| 使用索引 | 是 | 是 | 否 |
| 多作業並發寫 | 允許 | 不允許 | 允許 |
| 元數據位置 | master | 根目錄文件夾 | 根目錄文件夾 |
| 版本回滾 | 不支持 | 通過設置增量視圖的INSTANTTIME范圍,支持版本回滾 | 自帶Time Travel功能,支持版本回滾 |
| 實時性 | kudu使用內存存儲新增數據,實時性相對較高 | hudi有寫優化存儲方式,能達到1-5分鍾延遲的近實時處理 | delta lake必須完成commit提交才能查詢到新增數據,實時性差 |
| 支持hadoop文件系統 | 不支持,kudu通過raft管理自己的存儲服務器 | 支持 | 支持 |
| 缺省列處理 | 默認為null | hudi在插入時必須指定所有字段,否則報錯 | 默認為null |
| 並發讀寫 | 支持 | 不支持多客戶端同時寫 | 支持 |
| 兼容性 | 與impala集成支持sql,支持spark讀寫 | 支持spark、presto、hive、mapreduce,兼容性較好 | 深度依賴spark,目前有限支持hive、presto,有待完善 |
如何選擇合適的存儲方案
kudu
-
不同於hudi和delta lake是作為數據湖的存儲方案,kudu設計的初衷是作為hive和hbase的折中,因此它同時具有隨機讀寫和批量分析的特性。
-
kudu允許對不同列使用單獨的編碼和壓縮格式,擁有強大的索引支持,搭配range分區和hash分區的合理划分,
對分區查看、擴容和數據高可用性的支持都非常好,適用於既有隨機訪問,也有批量數據掃描的復合場景。 -
kudu可以和impala、spark集成,支持sql操作,除此之外,kudu能夠充分發揮高性能存儲設備的優勢。
-
相比較其他兩者,kudu不支持雲存儲,也不支持版本回滾和增量處理。
hudi
-
hudi的產生背景是為了解決Uber的增量更新問題,它提供了豐富的視圖和存儲優化方式,
可以適配批量訪問場景,增量處理場景,以及近實時查詢場景,無論是讀多寫少還是讀少寫多,hudi都能提供對應的優化方案,
用戶可以根據自身場景靈活選擇合適的配置。 -
三者之中,hudi的兼容性最好,它原生支持spark、presto、hive和mapreduce等大數據生態系統,並且讀寫底層文件實現了自己的InputFormat,更容易與其它系統做兼容。
-
hudi目前還不支持通過sql操作數據,(19年12月)社區已經將其作為下一步的方向,但完成時間不確定。
-
hudi不存在鎖機制,因此不支持多客戶端同時寫一張表,這是需要注意的一點。
delta lake
-
delta lake深度綁定spark,支持版本回滾,在寫入的時候對數據進行合並,因此對讀取比較友好。
-
delta lake有樂觀鎖機制,允許並發寫操作。但目前源碼中對沖突檢測很嚴格,對多用戶同時更新的場景支持並不好,適用於寫少讀多(或者only append寫多更新少)的場景。
-
delta lake開源版目前基本不支持sql操作數據(0.5版本支持hive和presto讀數據),應該在spark3.0發布后才會支持delete、update和merge操作。
-
開源版目前還存在小文件合並問題(商業版有優化,但是因為涉及到Databricks的Runtime功能,所以不准備開源,因此得自己手動合並小文件)。
-
總體來說,delta lake是一個較為優秀的數據湖存儲方案,但直接使用開源版本還需要用戶稍微進行一些針對性的優化。
