總述
Hudi提供兩類型表:寫時復制(Copy on Write, COW)表和讀時合並(Merge On Read, MOR)表。
對於Copy-On-Write Table,用戶的update會重寫數據所在的文件,所以是一個寫放大很高,但是讀放大為0,適合寫少讀多的場景。
對於Merge-On-Read Table,整體的結構有點像LSM-Tree,用戶的寫入先寫入到delta data中,這部分數據使用行存,這部分delta data可以手動merge到存量文件中,整理為parquet的列存結構。

數據計算模型
hudi是Uber主導開發的開源數據湖框架,所以大部分的出發點都來源於Uber自身場景,比如司機數據和乘客數據通過訂單ID來做join等。
在hudi過去的使用場景里,和大部分公司的架構類似,采用批式和流式共存的Lambda架構,后來Uber提出增量Incremental模型,相對批式來講,更加實時,相對流式而言,更加經濟。

1.批式模型(Batch)
批式模型就是使用MapReduce、Hive、Spark等典型的批計算引擎,以小時任務或者天任務的形式來做數據計算
特性
A.延遲:小時級延遲或者天級別延遲。這里的延遲不單單指的是定時任務的時間,在數據架構里,這里的延遲時間通常是定時任務間隔時間+一系列依賴任務的計算時間+數據平台最終可以展示結果的時間。數據量大、邏輯復雜的情況下,小時任務計算的數據通常真正延遲的時間是2-3小時。
B.數據完整度:數據較完整。以處理時間為例,小時級別的任務,通常計算的原始數據已經包含了小時內的所有數據,所以得到的數據相對較完整。但如果業務需求是事件時間,這里涉及到終端的一些延遲上報機制,在這里,批式計算任務就很難派上用場。
C.成本:成本很低。只有在做任務計算時,才會占用資源,如果不做任務計算,可以將這部分批式計算資源出讓給在線業務使用。從另一個角度來說成本是挺高的,如原始數據做了一些增刪改查,數據晚到的情況,那么批式任務是要全量重新計算。

2.流式模型(Stream)
流式模型,典型的就是使用Flink來進行實時的數據計算
特性
A.延遲:很短,甚至是實時。
B.數據完整度:較差。因為流式引擎不會等到所有數據到齊之后再開始計算,所以有一個watermark的概念,當數據的時間小於watermark時,就會被丟棄,這樣是無法對數據完整度有一個絕對的保障。在互聯網場景中,流式模型主要用於活動時的數據大盤展示,對數據的完整度要求並不算很高。在大部分場景中,用戶需要開發兩個程序,一是流式數據生產流式結果,而是批式計算人物,用於次日修復實時結果
C.成本:很高。因為流式任務時常駐的,並且對於多流join的場景,通常要借助內存或者數據庫來做state的存儲,不管是序列化開銷,還是和外部組件交互產生的額外IO,在大數據量下都是不容忽視的。

3.增量模型(Incremental)
針對批式和流式的優缺點,Uber提出了增量模型(Incremental Mode),相對批式來講,更加實時;相對流式而言,更加經濟。

增量模型,簡單來講,就是一mini batch的形式來跑准實時任務。hudi在增量模型中支持了兩個最重要的特性:
A.Upsert:這個主要是解決批式模型中,數據不能插入、更新的問題,有了這個特性,可以往Hive中寫入增量數據,而不是每次進行完全的覆蓋。(hudi自身維護了key-file的映射,所以當upsert時很容易找到key對應的文件)
B.Incremental Query:增量查詢,減少計算的原始數據量。以uber中司機和乘客的數據流join為例,每次抓取兩條數據流中的增量數據進行批式的join即可,相比流式數據而言,成本要降低幾個數量級。
查詢類型(Query Type)
Hudi支持三種不同的查詢表的方式:Snapshot Queries(快照查詢)、Incremental Queries(增量查詢)和Read Optimized Queries(讀優化查詢).

1.Snapshot Queries(快照查詢)
查詢某個增量提交操作中數據集的最新快照,先進行動態合並最新的基本文件(parquet)和增量文件(Avro)來提供近實時數據集(通常會存在幾分鍾的延遲)
讀取所有partition下每個FileGroup最新的FileSlice中的文件,Copy On Write表讀parquet文件,Merge On Read表讀parquet + log文件

2.Incremental Queries(增量查詢)
僅查詢新寫入數據集的文件,需要指定一個Commit/Compaction的即時時間(位於Timeline上的某個instant)作為條件,來查詢此條件之后的新數據
可查看自給定commit/delta commit即時操作依賴新寫入的數據,有效地提供變更流來啟用增量數據管道

3.Read Optimized Queries(讀優化查詢)
直接查詢基本文件(數據集的最新快照),其實就是列式文件(Parquet)。並保證與非hudi列式數據集相比,具有相同的列式查詢性能
可查看給定的commit/compact即時操作的表的最新快照
讀優化查詢和快照查詢相同僅訪問基本文件,提供給定文件片自上次執行壓縮操作以來的數據。通常查詢數據的最新程度的保證取決於壓縮策略

表類型解析
1.Copy On Write
簡稱COW,它實在數據寫入的時候,復制一份原來的拷貝,在其基礎上添加新數據
正在讀數據的請求,讀取的是最近的完整副本,這類似於MySQL的MVCC思想


*優點:讀取時,只讀取對應分區的一個數據文件即可,較為高效
*缺點:數據寫入的時候,需要復制一個先前的副本再在其基礎上生成新的數據文件,這個過程比較耗時
COW表主要使用列式文件格式(parquet)存儲數據,在寫入數據過程中,執行同步合並,更新數據版本並重寫數據文件,類似RDBMS中的B-Tree更新
A.更新update:在更新記錄時,hudi會先找到包含更新數據的文件,然后再使用更新值(最新的數據)重寫該文件,包含其他記錄的文件保持不變。當突然有大量寫操作時會導致重寫大量文件,從而導致極大的IO開銷
B.讀取read:在讀取數據時,通過讀取最新的數據文件來獲取最新的更新,此存儲類型適用於少量寫入和大量讀取的場景
2.Merge On Read
簡稱MOR,新插入的數據存儲在delta log中,定期再將delta log合並進行parquet數據文件
讀取數據時,會將delta log跟老的數據文件做merge,得到完整的數據返回



*優點:由於寫入數據先寫delta log,且delta log較小,所以寫入成本較低
*缺點:需要定期合並整理compact,否則碎片文件較多。讀取性能較差,因為需要將delta log和老數據文件合並
MOR表是COW表的升級版,他使用列式(parquet)與行式(avro)文件混合的方式存儲數據。在更新記錄時,類似nosql中的LSM-Tree更新
A.更新:在更新記錄時,僅更新到增量文件(avro)中,然后進行異步(或同步)的從沒怕餐廳,最后創建列式文件(parquet)的新版本。次存儲類型適合頻繁寫的工作負載,因為新紀錄是以追加的模式寫入增量文件中
B.讀取:在讀取數據集時,需要先將增量文件與舊文件進行合並,然后聖承列式文件成功后,在進行查詢
COW vs MOR
對於寫時復制(COW)和讀時合並(MOR)writer來說,Hudi的WriteClient是相同的。
COW表:用戶在snapshot讀取的時候會掃描所有最新的FileSlice的base file
MOR表:在READ OPTIMIZED模式下,只會讀最近的經過compaction的commit
