以下文章來自微信官方賬號——3分鐘看懂大數據,作者穿越其中。
在Flink實時流中,經常通過Flink CDC插件讀取Mysql數據,然后寫入胡迪。因此,在進行上述操作時,需要了解胡迪的基本概念和操作原理,以便在近乎實時地將數據寫入胡迪時,可以及時處理錯誤問題。
接下來,將從以下幾個方面全面闡述胡迪元器件的核心知識點。
1.數據湖和數據倉庫有什么區別?
2.2的基本功能。胡迪
3胡迪數據管理
4胡迪核心觀點分析
這篇文章的PDF版本已經整理好了。關注下方微信官方賬號【大數據左右手】,回復關鍵詞【進群】】獲取PDF。
1數據湖和數據倉庫的區別?
數據倉庫
數據倉庫(英文:Data Warehouse,簡稱DW)是存儲、分析、報告的數據系統.的數據倉庫
數據倉庫的目的是在面向分析建立一個集成的數據環境,并分析為企業提供決策支持(Decision Support).的結果
數據湖
數據湖就像數據庫和數據倉庫一樣,是數據存儲.的設計模式,?,F在,企業的數據倉庫將通過分層的方式.將數據存儲在文件夾和文件中
數據湖是集中式,的一個數據存儲庫,在,用來存儲,有大量的原始數據,在,使用有一個存儲數據的平面結構。
定義:在原始格式(通常是對象塊或文件)中存儲數據的系統或存儲庫,通常是所有企業數據的單一存儲。
數據湖可以包括來自關系數據庫的結構化數據(行和列)、半結構化數據(CSV、日志、XML、JSON)、非結構化數據(電子郵件、文檔、pdf)和二進制數據(圖像、音頻、視頻)。
湖中的數據,用于報告、可視化、高級分析和機器學習等任務。
C17C2DE64F93072A92F5.jpg">兩者的區別:
數據倉庫是一個優化的數據庫,用于分析來自事務系統和業務線應用程序的關系數據。
數據湖存儲來自業務線應用程序的關系數據,以及來自移動應用程序、IoT 設備和社交媒體的非關系數據。

- 數據湖并不能替代數據倉庫,數據倉庫在高效的報表和可視化分析中仍有優勢。
2 Hudi 基礎功能
2.1 Hudi 簡介
Apache Hudi 由 Uber 開發并開源,該項目在 2016 年開始開發,并于 2017 年開源,2019年 1 月進入 Apache 孵化器,且 2020 年 6 月稱為 Apache 頂級項目,目前最新版本:0.10.1 版本。
Hudi 一開始支持 Spark 進行數據攝入(批量 Batch 和流式 Streaming),從 0.7.0 版本開始,逐漸與 Flink 整合,主要在于 Flink SQL 整合,還支持 Flink SQL CDC。

Hudi(Hadoop Upserts anD Incrementals縮寫)是目前市面上流行的三大開源數據湖方案之一。
用于管理分布式文件系統 DFS 上大型分析數據集存儲。
簡單來說,Hudi 是一種針對分析型業務的、掃描優化的數據存儲抽象,它能夠使 DFS 數據集在分鐘級的時延內支持變更,也支持下游系統對這個數據集的增量處理。
2.2 Hudi 功能
Hudi 是在大數據存儲上的一個數據集,可以將 Change Logs 通過 upsert的方式合并進 Hudi;
Hudi 對上可以暴露成一個普通 Hive 或 Spark 表,通過 API 或命令行可以獲取到增量修改的信息,繼續供下游消費;
Hudi 保管修改歷史,可以做時間旅行或回退;
Hudi 內部有主鍵到文件級的索引,默認是記錄到文件的布隆過濾器;

2.3 Hudi 的特性
Apache Hudi 使得用戶能在 Hadoop 兼容的存儲之上存儲大量數據,同時它還提供兩種原語,不僅可以批處理,還可以在數據湖上進行流處理。
Update/Delete 記錄:Hudi 使用細粒度的文件/記錄級別索引來支持 Update/Delete 記錄,同時還提供寫操作的事務保證。查詢會處理最后一個提交的快照,并基于此輸出結果。
變更流:Hudi 對獲取數據變更提供了一流的支持:可以從給定的 時間點獲取給定表中已 updated / inserted / deleted 的所有記錄的增量流,并解鎖新的查詢姿勢(類別)。
- Apache Hudi 本身不存儲數據,僅僅管理數據。
- Apache Hudi 也不分析數據,需要使用計算分析引擎,查詢和保存數據,比如 Spark 或 Flink;
- 使用 Hudi 時,加載 jar 包,底層調用 API,所以需要依據使用大數據框架版本,編譯 Hudi 源碼,獲取對應依賴jar包。

2.4 Hudi 的 架構

通過 DeltaStreammer、Flink、Spark 等工具,將數據攝取到數據湖存儲,可使用HDFS 作為數據湖的數據存儲;
基于 HDFS 可以構建 Hudi 的數據湖;
Hudi 提供統一的訪問 Spark 數據源和 Flink 數據源;
外部通過不同引擎,如:Spark、Flink、Presto、Hive、Impala、Aliyun DLA、AWS Redshit 訪問接口;

2.5 湖倉一體架構
Hudi 對于 Flink 友好支持以后,可以使用 Flink + Hudi 構建實時湖倉一體架構,數據的時效性可以到分鐘級,能很好的滿足業務準實時數倉的需求。
通過湖倉一體、流批一體,準實時場景下做到了:數據同源、同計算引擎、同存儲、同計算口徑。

3 Hudi 數據管理
3.1 Hudi 表數據結構
Hudi 表的數據文件,可以使用操作系統的文件系統存儲,也可以使用 HDFS 這種分布式的文件系統存儲。為了后續分析性能和數據的可靠性,一般使用 HDFS 進行存儲。以 HDFS 存儲來看,一個 Hudi 表的存儲文件分為兩類。

.hoodie 文件:由于 CRUD 的零散性,每一次的操作都會生成一個文件,這些小文件越來越多后,會嚴重影響 HDFS 的性能,Hudi 設計了一套文件合并機制。.hoodie 文件夾中存放了對應的 **文件合并操作 **相關的日志文件。
amricas 和 asia 相關的路徑是 實際的數據文件,按分區存儲,分區的路徑 key 是可以指定的。
3.1.1 .hoodie 文件
Hudi 把隨著時間流逝,對表的一系列 CRUD 操作叫做 Timeline,Timeline 中某一次的操作,叫做 Instant。
Hudi 的核心是維護 **Timeline **在不同時間對表執行的所有操作,instant 這有助于提供表的即時視圖,同時還有效地支持按到達順序檢索數據。Hudi Instant 由以下組件組成:
Instant Action: 記錄本次操作是一次操作類型 數據提交(COMMITS),還是文件合并(COMPACTION),或者是文件清理(CLEANS);
Instant Time,本次操作發生的時間,通常是時間戳(例如:20190117010349),它按照動作開始時間的順序單調遞增。
lState,操作的狀態,發起(REQUESTED),進行中(INFLIGHT),還是已完成(COMPLETED);
.hoodie文件夾中存放對應操作的狀態記錄:

3.1.2 數據文件
Hudi 真實的數據文件使用 Parquet 文件格式存儲

其中包含一個 metadata 元數據文件和數據文件 parquet 列式存儲。
Hudi 為了實現數據的 CRUD,需要能夠唯一標識一條記錄,Hudi 將把數據集中的 唯一字段(record key ) + 數據所在分區 (partitionPath)聯合起來當做 數據的唯一鍵。
3.2 數據存儲概述
Hudi 數據集的 組織目錄結構與 Hive 表示非常相似,一份數據集對應這一個根目錄。數據集被 打散為多個分區,分區字段以文件夾形式存在,該文件夾包含該分區的所有文件。

在根目錄下,每個分區都有唯一的分區路徑,每個分區數據存儲在多個文件中。

每個文件都有唯一的 fileId 和生成文件的 commit 標識。如果發生更新操作時,多個文件共享相同的 fileId,但會有不同的 commit。
3.3 Metadata 元數據
以時間軸(Timeline)的形式將數據集上的各項操作元數據維護起來,以支持數據集的瞬態視圖,這部分元數據存儲于根目錄下的元數據目錄。一共有三種類型的元數據:
Commits:一個單獨的commit包含對數據集之上一批數據的一次原子寫入操作的相關信息。我們用單調遞增的時間戳來標識commits,標定的是一次寫入操作的開始。
Cleans:用于清除數據集中不再被查詢所用到的舊版本文件的后臺活動。
Compactions:用于協調Hudi內部的數據結構差異的后臺活動。例如,將更新操作由基于行存的日志文件歸集到列存數據上

3.4 Index 索引
Hudi 維護著一個索引,以支持在記錄 key 存在情況下,將新記錄的 key 快速映射到對應的fileId。
Bloom filter:存儲于數據文件頁腳。默認選項,不依賴外部系統實現。數據和索引始終保持一致。
Apache HBase :可高效查找一小批 key。在索引標記期間,此選項可能快幾秒鐘。

3.4.1 索引策略
工作負載 1:對事實表
許多公司將大量事務數據存儲在 NoSQL 數據存儲中。例如,拼車情況下的行程表、股票買賣、電子商務網站中的訂單。這些表通常會隨著對最新數據的隨機更新而不斷增長,而長尾更新會針對較舊的數據,這可能是由于交易在以后結算/數據更正所致。換句話說,大多數更新進入最新的分區,很少有更新進入較舊的分區。
圖1:事實表的典型更新模式
對于這樣的工作負載,BLOOM 索引表現良好,因為索引查找 將基于大小合適的布隆過濾器修剪大量數據文件。此外,如果可以構造鍵以使它們具有一定的順序,則要比較的文件數量會通過范圍修剪進一步減少。
Hudi 使用所有文件鍵范圍構建一個區間樹,并有效地過濾掉更新/刪除記錄中與任何鍵范圍不匹配的文件。
為了有效地將傳入的記錄鍵與布隆過濾器進行比較,即最小數量的布隆過濾器讀取和跨執行程序的統一工作分配,Hudi 利用輸入記錄的緩存并采用可以使用統計信息消除數據偏差的自定義分區器。有時,如果布隆過濾器誤報率很高,它可能會增加混洗的數據量以執行查找。
Hudi 支持動態布隆過濾器(使用啟用 hoodie.bloom.index.filter.type=DYNAMIC_V0),它根據存儲在給定文件中的記錄數調整其大小,以提供配置的誤報率。
工作負載 2:對事件表
事件流無處不在。來自 Apache Kafka 或類似消息總線的事件通常是事實表大小的 10-100 倍,并且通常將 時間(事件的到達時間/處理時間)視為一等公民。
例如,**物聯網事件流、點擊流數據、廣告印象 **等。插入和更新僅跨越最后幾個分區,因為這些大多是僅附加數據。鑒于可以在端到端管道中的任何位置引入重復事件,因此在存儲到數據湖之前進行重復數據刪除是一項常見要求。

一般來說,這是一個非常具有挑戰性的問題,需要以較低的成本解決。雖然,我們甚至可以使用鍵值存儲來使用 HBASE 索引執行重復數據刪除,但索引存儲成本會隨著事件的數量線性增長,因此可能會非常昂貴。
實際上,BLOOM 帶有范圍修剪的索引是這里的最佳解決方案。人們可以利用時間通常是一等公民這一事實并構造一個鍵,event_ts + event_id 例如插入的記錄具有單調遞增的鍵。即使在最新的表分區中,也可以通過修剪大量文件來產生巨大的回報。
工作負載 3:隨機更新/刪除維度表
這些類型的表格通常包含高維數據并保存參考數據,例如 用戶資料、商家信息。這些是高保真表,其中更新通常很小,但也分布在許多分區和數據文件中,數據集從舊到新。通常,這些表也是未分區的,因為也沒有對這些表進行分區的好方法。
如前所述,BLOOM 如果無法通過比較范圍/過濾器來刪除大量文件,則索引可能不會產生好處。在這樣的隨機寫入工作負載中,更新最終會觸及表中的大多數文件,因此布隆過濾器通常會根據一些傳入的更新指示所有文件的真陽性。因此,我們最終會比較范圍/過濾器,只是為了最終檢查所有文件的傳入更新。
SIMPLE 索引將更適合,因為它不進行任何基于預先修剪的操作,而是直接與每個數據文件中感興趣的字段連接 。HBASE 如果操作開銷是可接受的,并且可以為這些表提供更好的查找時間,則可以使用索引。
在使用全局索引時,用戶還應該考慮設置 hoodie.bloom.index.update.partition.path=true或hoodie.simple.index.update.partition.path=true 處理分區路徑值可能因更新而改變的情況,例如用戶表按家鄉分區;用戶搬遷到不同的城市。這些表也是 Merge-On-Read 表類型的絕佳候選者。
3.5 Data 數據
Hudi 以兩種不同的存儲格式存儲所有攝取的數據,用戶可選擇滿足下列條件的任意數據格式:
讀優化的列存格式(ROFormat):缺省值為 Apache Parquet;
寫優化的行存格式(WOFormat):缺省值為 Apache Avro;

4 Hudi 核心點解析
4.1 基本概念
Hudi 提供了Hudi 表的概念,這些表支持 CRUD 操作,可以利用現有的大數據集群比如 HDFS 做數據文件存儲,然后使用 SparkSQL 或 Hive 等分析引擎進行數據分析查詢。

Hudi 表的三個主要組件:
1) 有序的時間軸元數據,類似于數據庫事務日志。
2) 分層布局的數據文件:實際寫入表中的數據;
3)索引(多種實現方式):映射包含指定記錄的數據集。
4.1.1 時間軸Timeline
Hudi 核心:
在所有的表中維護了一個包含在不同的即時(Instant)時間對數據集操作(比如新增、修改或刪除)的時間軸(Timeline)。
在每一次對 Hudi 表的數據集操作 時都會在該表的 Timeline 上生成一個 Instant,從而可以實現在僅查詢某個時間點之后成功提交的數據,或是僅查詢某個時間點之前的數據,有效避免了掃描更大時間范圍的數據。
可以高效地只查詢更改前的文件(如在某個Instant提交了更改操作后,僅query某個時間點之前的數據,則仍可以query修改前的數據)。

Timeline 是 Hudi 用來管理提交(commit)的抽象,每個 commit 都綁定一個固定時間戳,分散到時間線上。
在 Timeline 上,每個 commit 被抽象為一個 HoodieInstant,一個 instant 記錄了一次提交 (commit) 的行為、時間戳、和狀態。

圖中采用時間(小時)作為分區字段,從 10:00 開始陸續產生各種 commits,10:20 來了一條 9:00 的數據,該數據仍然可以落到 9:00 對應的分區,通過 timeline 直接消費 10:00 之后的增量更新(只消費有新 commits 的 group),那么這條延遲的數據仍然可以被消費到。
時間軸(Timeline)的實現類(位于hudi-common-xx.jar中),時間軸相關的實現類位于 org.apache.hudi.common.table.timeline 包下.

4.1.2 文件管理
Hudi 將 DFS 上的數據集組織到基本路徑(HoodieWriteConfig.BASEPATHPROP)下的目錄結構中。
數據集分為多個分區(DataSourceOptions.PARTITIONPATHFIELDOPT_KEY),這些分區與Hive表非常相似,是包含該分區的數據文件的文件夾。

在每個分區內,文件被組織為文件組,由文件 id 充當唯一標識。每個文件組包含多個文件切片,其中每個切片包含在某個即時時間的提交/壓縮生成的基本列文件(.parquet)以及一組日志文件(.log),該文件包含自生成基本文件以來對基本文件的插入/更新。

Hudi 的 base file (parquet 文件) 在 footer 的 meta 去記錄了 record key 組成的 BloomFilter,用于在 file based index 的實現中實現高效率的 key contains 檢測。
Hudi 的 log (avro 文件)是自己編碼的,通過積攢數據 buffer 以 LogBlock 為單位寫出,每個 LogBlock 包含 magic number、size、content、footer 等信息,用于數據讀、校驗和過濾。

4.1.3 索引 Index
- Hudi通過索引機制提供高效的Upsert操作,該機制會將一個RecordKey+PartitionPath組合的方式作為唯一標識映射到一個文件ID,而且這個唯一標識和文件組/文件ID之間的映射自記錄被寫入文件組開始就不會再改變。
- 全局索引:在全表的所有分區范圍下強制要求鍵保持唯一,即確保對給定的鍵有且只有一個對應的記錄。
- 非全局索引:僅在表的某一個分區內強制要求鍵保持唯一,它依靠寫入器為同一個記錄的更刪提供一致的分區路徑。

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

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

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

4.2.1.3 增量模型(Incremental)
針對批式和流式的優缺點,Uber 提出了增量模型(Incremental Mode),相對批式來講,更加實時;相對流式而言,更加經濟。
增量模型,簡單來講,是以 mini batch 的形式來跑準實時任務。Hudi 在增量模型中支持了兩個最重要的特性:
Upsert:這個主要是解決批式模型中,數據不能插入、更新的問題,有了這個特性,可以往 Hive 中寫入增量數據,而不是每次進行完全的覆蓋。(Hudi 自身維護了 key->file 的映射,所以當 upsert 時很容易找到 key 對應的文件)
Incremental Query:增量查詢,減少計算的原始數據量。以 Uber 中司機和乘客的數據流 Join 為例,每次抓取兩條數據流中的增量數據進行批式的 Join 即可,相比流式數據而言,成本要降低幾個數量級。

4.2.2 查詢類型(Query Type)
Hudi支持三種不同的查詢表的方式:Snapshot Queries、Incremental Queries和Read Optimized Queries。

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

4.2.2.2 增量查詢(Incremental Queries)
類型二:Incremental Queries(增量查詢)
僅查詢新寫入數據集的文件,需要指定一個Commit/Compaction的即時時間(位于Timeline上的某個Instant)作為條件,來查詢此條件之后的新數據。
可查看自給定commit/delta commit即時操作以來新寫入的數據,有效的提供變更流來啟用增量數據管道。

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

4.2.3 Hudi 支持表類型
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 的列存結構。

4.2.3.1 寫時復制表(COW)
Copy on Write 簡稱 COW,顧名思義,它是在數據寫入的時候,復制一份原來的拷貝,在其基礎上添加新數據。
正在讀數據的請求,讀取的是最近的完整副本,這類似Mysql 的MVCC的思想。

優點:讀取時,只讀取對應分區的一個數據文件即可,較為高效;
缺點:數據寫入的時候,需要復制一個先前的副本再在其基礎上生成新的數據文件,這個過程比較耗時

COW表主要使用列式文件格式(Parquet)存儲數據,在寫入數據過程中,執行同步合并,更新數據版本并重寫數據文件,類似RDBMS中的B-Tree更新。
更新update:在更新記錄時,Hudi會先找到包含更新數據的文件,然后再使用更新值(最新的數據)重寫該文件,包含其他記錄的文件保持不變。當突然有大量寫操作時會導致重寫大量文件,從而導致極大的I/O開銷。
讀取read:在讀取數據時,通過讀取最新的數據文件來獲取最新的更新,此存儲類型適用于少量寫入和大量讀取的場景
4.2.3.2 讀時合并表(MOR)
Merge On Read簡稱MOR,新插入的數據存儲在delta log 中,定期再將delta log合并進行parquet數據文件。
讀取數據時,會將delta log跟老的數據文件做merge,得到完整的數據返回。下圖演示了MOR的兩種數據讀寫方式

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

4.2.4 數據寫操作類型
在 Hudi 數據湖框架中支持三種方式寫入數據:UPSERT(插入更新)、INSERT(插入)和BULK INSERT(寫排序)。
UPSERT:默認行為,數據先通過 index 打標(INSERT/UPDATE),有一些啟發式算法決定消息的組織以優化文件的大小
INSERT:跳過 index,寫入效率更高
BULK**_INSERT**:寫排序,對大數據量的 Hudi 表初始化友好,對文件大小的限制 best effort(寫 HFile)

4.2.4.1 寫流程(upsert)
(1)Copy On Write類型表,UPSERT 寫入流程
第一步、先對 records 按照 record key 去重;
第二步、首先對這批數據創建索引 (HoodieKey => HoodieRecordLocation);通過索引區分哪些 records 是 update,哪些 records 是 insert(key 第一次寫入);
第三步、對于 update 消息,會直接找到對應 key 所在的最新 FileSlice 的 base 文件,并做 merge 后寫新的 base file (新的 FileSlice);
第四步、對于 insert 消息,會掃描當前 partition 的所有 SmallFile(小于一定大小的 base file),然后 merge 寫新的 FileSlice;如果沒有 SmallFile,直接寫新的 FileGroup + FileSlice;
(2)Merge On Read類型表,UPSERT 寫入流程
第一步、先對 records 按照 record key 去重(可選)
第二步、首先對這批數據創建索引 (HoodieKey => HoodieRecordLocation);通過索引區分哪些 records 是 update,哪些 records 是 insert(key 第一次寫入)
第三步、如果是 insert 消息,如果 log file 不可建索引(默認),會嘗試 merge 分區內最小的 base file (不包含 log file 的 FileSlice),生成新的 FileSlice;如果沒有 base file 就新寫一個 FileGroup + FileSlice + base file;如果 log file 可建索引,嘗試 append 小的 log file,如果沒有就新寫一個 FileGroup + FileSlice + base file
第四步、如果是 update 消息,寫對應的 file group + file slice,直接 append 最新的 log file(如果碰巧是當前最小的小文件,會 merge base file,生成新的 file slice)log file 大小達到閾值會 roll over 一個新的
4.2.4.2 寫流程(Insert)
(1) Copy On Write類型表,INSERT 寫入流程
第一步、先對 records 按照 record key 去重(可選);
第二步、不會創建 Index;
第三步、如果有小的 base file 文件,merge base file,生成新的 FileSlice + base file,否則直接寫新的 FileSlice + base file;
(2) Merge On Read類型表,INSERT 寫入流程
第一步、先對 records 按照 record key 去重(可選);
第二步、不會創建 Index;
第三步、如果 log file 可索引,并且有小的 FileSlice,嘗試追加或寫最新的 log file;如果 log file 不可索引,寫一個新的 FileSlice + base file。