文章目錄
- 1.時間軸 (Timeline)
- 2.檔案的布局 (File Layout)
- 2.1 檔案的存儲方式
- 2.2 檔案的管理
- 3.索引 (Index)
- 3.1 索引選項
- 3.2 全局索引與非全局索引
- 3.3 索引的選擇政策
- 4.表類型(Table Types)
- 4.1 Copy On Write
- 4.2 Merge On Read
- 5.查詢類型(Query Types)
1.時間軸 (Timeline)
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CMyYDN5EDZlhTY3cjMmJTNzYzX3QjNyMDMyIzLcBTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
Hudi的核心是維護不同時刻在表上執行的所有操作的時間表, 提供表的即時視圖, 同時還有效的支援按照時間順序檢索資料, Hudi的時刻組成:
- Instant action: 在表上執行的操作類型
- Instant time: 即時時間,通常是一個時間戳,它安裝action的開始時間單調遞增
- State: 時刻的目前狀态
hudi保證在時間線上的操作都是基于即時時間的,兩者的時間保持一緻并且是原子性的。
action:
- commits: 表示将一批資料原子寫入表中
- cleans: 清除表中不在需要的舊版本檔案的背景活動。
- delta_commit:增量送出是指将一批資料原子性寫入MergeOnRead類型的表中,其中部分或者所有資料可以寫入增量日志中。
- compaction: 協調hudi中差異資料結構的背景活動,例如:将更新從基于行的日志檔案變成列格式。在内部,壓縮的表現為時間軸上的特殊送出。
- rollback:表示送出操作不成功且已經復原,會删除在寫入過程中産生的資料
- savepoint:将某些檔案标記為“已儲存”,以便清理程式時不會被清楚。在需要資料恢複的情況下,有助于将資料集還原到時間軸上某個點。
state:
- requested:表示一個動作已被安排,但尚未啟動
- inflight:表是目前正在執行操作
- completed:表是在時間線上完成了操作
上圖采用時間 小時 作為分區字段, 10:00 開始産生各種commits, 10:20 來了一條 9:00 的資料, 根據 event time 該資料被分到 9:00 的分區, 通過timeline直接消費10:00 之後的增量更新, 這條延遲的資料仍然可以消費到。
2.檔案的布局 (File Layout)
2.1 檔案的存儲方式
Hudi 的 存儲分為兩個部分:
- 中繼資料: .hoodie 目錄對應的是中繼資料, 包括表的版本管理 timeline, 歸檔目錄(存放過時的instant也就是版本),一個instant記錄了一次送出(commit)的行為、時間戳和狀态,Hudi以時間軸的形式維護了在資料集上執行的所有操作的中繼資料;
- 資料:和hive一樣,以分區方式存放資料;分區裡面存放着Base File(.parquet)和Log File(.log.*)
.hoodie 中繼資料 和 分區
.hoodie
存有deltacommit 增強送出的資訊, 和instance的資訊, 有time, state, action資訊
分區中的檔案為 .log 和 .parquet 和 .metadata 檔案
,metadata 檔案
.log資料檔案
2.2 檔案的管理
- Hudi将資料表組織成分布式檔案系統基本路徑(basepath)下的目錄結構
- 表被劃分為多個分區,這些分區是包含該分區的資料檔案的檔案夾,非常類似于Hive表
- 在每個分區中,檔案被組織成檔案組,由檔案ID唯一辨別
- 每個檔案組包含幾個檔案片(FileSlice)
-
每個檔案片包含:
一個基本檔案(.parquet):在某個commit/compaction即時時間(instant time)生成的(MOR可能沒有)
多個日志檔案(.log.*),這些日志檔案包含自生成基本檔案以來對基本檔案的插入/更新(COW沒有)
-
Hudi采用了多版本并發控制(Multiversion Concurrency Control, MVCC)
其中壓縮操作會将日志和基本檔案合并成新的檔案片,清理操作會将未使用/較舊的檔案片删除來回收DFS上的空間。
compaction操作:合并日志和基本檔案以産生新的檔案片
clean操作:清除不使用的/舊的檔案片以回收檔案系統上的空間
- Hudi的base file(parquet 檔案)在 footer 的 meta 去記錄了 record key 組成的 BloomFilter,用于在 file based index 的實作中實作高效率的 key contains 檢測。隻有不在 BloomFilter 的 key 才需要掃描整個檔案消滅假陽。
- Hudi 的 log (avro 檔案)是自己編碼的,通過積攢資料 buffer 以 LogBlock 為機關寫出,每個 LogBlock 包含 magic number、size、content、footer 等資訊,用于資料讀、校驗和過濾。
3.索引 (Index)
Hudi通過索引機制将映射的給定的hoodie key(record key+partition path)映射到檔案id(唯一标示),進而提供高效的upsert操作。記錄鍵和檔案組/檔案ID之間的這種映射,一旦記錄的第一個版本寫入檔案就永遠不會改變。
是以,一個 FileGroup 包含了一批 record 的所有版本記錄。Index 用于區分消息是 INSERT 還是 UPDATE。
有了索引機制,可以做到:避免讀取不需要的檔案、避免更新不必要的檔案、無需将更新資料與曆史資料做分布式關聯,隻需要在 File Group 内做合并。
3.1 索引選項
Flink隻有一種state based index(和bucket_index),其他index是Spark可選配置。
3.2 全局索引與非全局索引
- 全局索引:全局索引在全表的所有分區範圍下強制要求鍵的唯一性,也就是確定對給定的鍵有且隻有一個對應的記錄。全局索引提供了更強的保證,但是随着表增大,update/delete 操作損失的性能越高,是以更适用于小表。
- 非全局索引:預設的索引實作,隻能保證資料在分區的唯一性。非全局索引依靠寫入器為同一個記錄的update/delete提供一緻的分區路徑,同時大幅提高了效率,更适用于大表。
- 從index的維護成本和寫入性能的角度考慮,維護一個global index的難度更大,對寫入性能的影響也更大,是以需要non-global index。
HBase索引本質上是一個全局索引,bloom和simple index都有全局選項:
- hoodie.index.type=GLOBAL_BLOOM
- hoodie.index.type=GLOBAL_SIMPLE
3.3 索引的選擇政策
對事實表的延遲更新
許多公司會在NoSQL資料存儲中存放大量的交易資料。例如共享出行的行程表、股票買賣記錄的表、和電商的訂單表。這些表通常一直在增長,且大部分的更新随機發生在較新的記錄上,而對舊記錄有着長尾分布型的更新。這通常是源于交易關閉或者資料更正的延遲性。換句話說,大部分更新會發生在最新的幾個分區上而小部分會在舊的分區。
對于這樣的作業模式,
布隆索引
就能表現地很好,因為查詢索引可以靠設定得當的布隆過濾器來
裁剪很多資料檔案
。另外,如果生成的鍵可以以某種順序排列,參與比較的檔案數會進一步通過範圍裁剪而減少。Hudi用所有檔案的鍵域來構造區間樹,這樣能來高效地依據輸入的更删記錄的鍵域來排除不比對的檔案。
為了高效地把記錄鍵和布隆過濾器進行比對,即盡量減少過濾器的讀取和均衡執行器間的工作量,Hudi緩存了輸入記錄并使用了自定義分區器和統計規律來解決資料的偏斜。有時,如果布隆過濾器的假陽性率過高,查詢會增加資料的打亂操作。Hudi支援動态布隆過濾器(設定hoodie.bloom.index.filter.type=DYNAMIC_V0)。它可以根據檔案裡存放的記錄數量來調整大小進而達到設定的假陽性率。
對事件表的去重
事件流無處不在。從Apache Kafka或其他類似的消息總線發出的事件數通常是事實表大小的10-100倍。事件通常把時間(到達時間、處理時間)作為首類處理對象,比如物聯網的事件流、點選流資料、廣告曝光數等等。由于這些大部分都是僅追加的資料,插入和更新隻存在于最新的幾個分區中。由于重複事件可能發生在整個資料管道的任一節點,在存放到資料湖前去重是一個常見的需求。
總的來說,低消耗去重是一個非常有挑戰的工作。雖然可以用一個鍵值存儲來實作去重(即
HBase索引
),但索引存儲的消耗會随着事件數增長而線性增長以至于變得不可行。事實上,有範圍裁剪功能的布隆索引是最佳的解決方案。我們可以利用作為首類處理對象的時間來構造由事件時間戳和事件id(event_ts+event_id)組成的鍵,這樣插入的記錄就有了單調增長的鍵。這會在最新的幾個分區裡大幅提高裁剪檔案的效益。
對次元表的随機更删
正如之前提到的,如果範圍比較不能裁剪許多檔案的話,那麼布隆索引并不能帶來很好的效益。在這樣一個随機寫入的作業場景下,更新操作通常會觸及表裡大多數檔案進而導緻布隆過濾器依據輸入的更新對所有檔案标明陽性。最終會導緻,即使采用了範圍比較,也還是檢查了所有檔案。使用簡單索引對此場景更合适,因為它不采用提前的裁剪操作,而是直接和所有檔案的所需字段連接配接。如果額外的運維成本可以接受的話,也可以采用HBase索引,其對這些表能提供更加優越的查詢效率。
當使用全局索引時,也可以考慮通過設定hoodie.bloom.index.update.partition.path=true或hoodie.simple.index.update.partition.path=true來處理 的情況;例如對于以所在城市分區的使用者表,會有使用者遷至另一座城市的情況。這些表也非常适合采用Merge-On-Read表型。
4.表類型(Table Types)
- Copy on Write:使用列式存儲來存儲資料(例如:parquet),通過在寫入期間執行同步合并來簡單地更新和重制檔案, 在COW表中,隻有資料檔案/基本檔案(.parquet),沒有增量日志檔案(.log.*)。
-
Merge on Read:使用列式存儲(parquet)+行式檔案(arvo)組合存儲資料。更新記錄到增量檔案中,然後進行同步或異步壓縮來生成新版本的列式檔案。
MOR表中,包含列存的基本檔案(.parquet)和行存的增量日志檔案(基于行的avro格式,.log.*)。
4.1 Copy On Write
每一個新批次寫入都将建立相應資料檔案的新版本(新的FileSlice),新版本檔案包括舊版本檔案的記錄以及來自傳入批次的記錄(全量最新)
3 個檔案組,其中包含如下資料檔案
由于在寫入期間進行合并,COW 會産生一些寫入延遲。但是COW 的優勢在于它的簡單性,不需要其他表服務(如壓縮),也相對容易調試。
4.2 Merge On Read
Merge on Read表是copy on write的超集,它仍然支援通過僅向使用者公開最新的檔案切片中的基本/列來對表進行查詢優化。使用者每次對表檔案的upsert操作都會以增量日志的形式進行存儲,增量日志會對應每個檔案最新的ID來幫助使用者完成快照查詢。是以這種表類型,能夠智能平衡讀取和寫放大(wa),提供近乎實時的資料。這種表最重要的是壓縮器,它用來選擇将對應增量日志資料壓縮到表的基本檔案中,來保持查詢時的性能(較大的增量日志檔案會影響合并時間和查詢時間)
顧名思義,MOR表的合并成本在讀取端。是以在寫入期間我們不會合并或建立較新的資料檔案版本。标記/索引完成後,對于具有要更新記錄的現有資料檔案,Hudi 建立增量日志檔案并适當命名它們,以便它們都屬于一個檔案組。
讀取端将實時合并基本檔案及其各自的增量日志檔案。每次的讀取延遲都比較高(因為查詢時進行合并),是以 Hudi 使用壓縮機制來将資料檔案和日志檔案合并在一起并建立更新版本的資料檔案。
戶可以選擇内聯或異步模式運作壓縮。Hudi也提供了不同的壓縮政策供使用者選擇,最常用的一種是基于送出的數量。例如可以将壓縮的最大增量日志配置為 4。這意味着在進行 4 次增量寫入後,将對資料檔案進行壓縮并建立更新版本的資料檔案。壓縮完成後,讀取端隻需要讀取最新的資料檔案,而不必關心舊版本檔案。
- 對于 BloomFilter 這種無法對 log file 生成 index 的索引方案,對于 INSERT 消息仍然會寫 base file (parquet format),隻有 UPDATE 消息會 append log 檔案(因為 base file 已經記錄了該 UPDATE 消息的 FileGroup ID)。
- 對于可以對 log file 生成 index 的索引方案,例如 Flink writer 中基于 state 的索引,每次寫入都是 log format,并且會不斷追加和 roll over。
5.查詢類型(Query Types)
- Snapshot Queries:快照查詢,在此視圖上的查詢将看到某個送出和壓縮操作的最新快照。對于merge on read的表,它通過即時合并最新檔案切片的基本檔案和增量檔案來展示近乎實時的資料(幾分鐘)。對于copy on write的表,它提供了對現有parquet表的直接替代,同時提供了upsert/delete和其他寫入功能。
- Incremental Queries:增量查詢,該視圖智能看到從某個送出/壓縮寫入資料集的新資料。該視圖有效地提供了chang stream,來支援增量視圖
- Read Optimized Queries:讀優化視圖,在此視圖上的查詢将檢視到給定送出或壓縮操作中的最新快照。該視圖将最新檔案切片的列暴露個查詢,并保證與非hudi列式資料集相比,具有相同列式查詢功能。