問(wèn)題
- 什么是索引
- 索引作用
- 有哪些索引
- 全局索引與非全局索引區(qū)別
- Flink 支持哪些索引
前言
Hudi 系列文章在這個(gè)這里查看 https://github.com/leosanqing/big-data-study
索引(Index)是 Hudi 最重要的特性之一,也是區(qū)別于之前傳統(tǒng)數(shù)倉(cāng) Hive 的重要特點(diǎn), 是實(shí)現(xiàn) Time Travel, Update/Delete,事務(wù) 等重要特性的基礎(chǔ)
Hudi provides efficient upserts, by mapping a given hoodie key (record key + partition path) consistently to a file id, via an indexing mechanism
Hudi 通過(guò)索引機(jī)制提供了高效的 upsert, 索引機(jī)制是通過(guò)映射 HoodieKey( recordKey+partition) 與 File Id 實(shí)現(xiàn). 如果是非全局索引就不包括 partition
作用
- 減小開銷(也是實(shí)現(xiàn)數(shù)據(jù)更新的前提)
- Update/Delete 等數(shù)據(jù)變更的基礎(chǔ): Hudi的索引允許它知道在哪里可以找到給定的記錄砾肺,因此在執(zhí)行upsert或delete操作時(shí)硫朦,它可以直接訪問(wèn)和修改正確的數(shù)據(jù)文件丁眼,從而大大加速了這些操作秘豹。
- 事務(wù)支持:為了實(shí)現(xiàn)原子性的upsert和delete操作,Hudi維護(hù)了一個(gè)內(nèi)部的事務(wù)日志。索引確保在操作期間正確、有效地識(shí)別和處理數(shù)據(jù)。
- 增量查詢:除了提供對(duì)整個(gè)數(shù)據(jù)集的全量查詢外熄阻,Hudi還支持增量查詢,這使得用戶只查看自上次查詢以來(lái)對(duì)數(shù)據(jù)集所做的更改倔约。索引在此功能中也起到關(guān)鍵作用饺律,因?yàn)樗鼛椭粉櫮男┪募诵碌幕蚋牡挠涗洝?/li>
- 時(shí)間旅行和數(shù)據(jù)快照:Hudi支持?jǐn)?shù)據(jù)的多個(gè)版本,允許用戶“回溯”到數(shù)據(jù)的早期狀態(tài)跺株。這對(duì)于數(shù)據(jù)審計(jì)复濒、錯(cuò)誤恢復(fù)或分析數(shù)據(jù)的歷史變化非常有用。索引確保了數(shù)據(jù)版本之間的快速乒省、高效的轉(zhuǎn)換巧颈。
- 合并小文件(Clustering):在大數(shù)據(jù)生態(tài)系統(tǒng)中,小文件問(wèn)題是一個(gè)眾所周知的問(wèn)題袖扛。Hudi利用其索引能力合并小文件以優(yōu)化存儲(chǔ)和查詢性能砸泛。
減少開銷怎么理解
從上面官網(wǎng)的圖可以看出來(lái),沒(méi)有索引和有索引的開銷
如果沒(méi)有索引, 因?yàn)槲也⒉恢牢乙碌臄?shù)據(jù)在哪些文件中, 我每次的要實(shí)現(xiàn)更新需要訪問(wèn)所有的基礎(chǔ)文件, 需要這么多 IO 的開銷(100+25) * 8 = 1200MB
如果有索引, 我知道這些數(shù)據(jù)應(yīng)該更新到哪些基礎(chǔ)文件, 我只要找特定的文件就行, 所以開銷為 (100+252) * 4 = 600MB*
hive因?yàn)闆](méi)有索引,所以他不支持變更操作(update/Delete),一次寫入不能變更. 因?yàn)槟呐伦兏粭l數(shù)據(jù), 我都需要訪問(wèn)hdfs 上所有的文件(如果沒(méi)有分區(qū),有分區(qū)的話訪問(wèn)特定的分區(qū)下的所有文件),挨個(gè)比較主鍵, 然后重寫之后上傳到 hdfs
數(shù)據(jù)變更基礎(chǔ)
因?yàn)槲易兏臅r(shí)候知道了我這個(gè)數(shù)據(jù)應(yīng)該去哪個(gè)文件找, 重寫的成本就能接受了. 并且 Upsert 的時(shí)候我也能根據(jù)索引判斷, 我這條寫進(jìn)來(lái)的數(shù)據(jù)應(yīng)該是 Insert 還是應(yīng)該 Update
以 COW 表,upsert 寫入為例(當(dāng)然上述步驟會(huì)根據(jù)索引類型和計(jì)算引擎有不同的實(shí)現(xiàn)和步驟,但是大體為上面的步驟)
數(shù)據(jù)進(jìn)來(lái) --> 計(jì)算主鍵 --> 根據(jù)主鍵查詢索引判斷是 Insert 還是 Update --> 根據(jù) Insert 還是 Update 標(biāo)記寫入文件 -- >寫入時(shí), 寫到標(biāo)記的文件,更新的更新,插入的插入--> 更新索引
類型
重要的索引類型具體會(huì)放在源碼分析中詳細(xì)分析,這里只簡(jiǎn)單講個(gè)概念
- BLOOM: 采用根據(jù)RecordKey構(gòu)建的布隆過(guò)濾器,還可以選擇使用RecordKey范圍修剪候選文件蛆封。在分區(qū)內(nèi)強(qiáng)制執(zhí)行鍵唯一性唇礁。
- GLOBAL_BLOOM: 采用根據(jù)RecordKey構(gòu)建的布隆過(guò)濾器,還可以選擇使用RecordKey范圍修剪候選文件惨篱。表中的所有分區(qū)都強(qiáng)制執(zhí)行鍵唯一性盏筐。
- SIMPLE(Spark 引擎的默認(rèn)值): Spark 引擎的默認(rèn)索引類型。根據(jù)從存儲(chǔ)上的表中提取的鍵對(duì)傳入記錄執(zhí)行l(wèi)ean join砸讳。分區(qū)內(nèi)強(qiáng)制執(zhí)行鍵唯一性琢融。
- GLOBAL_SIMPLE: 根據(jù)從存儲(chǔ)上的表中提取的鍵對(duì)傳入記錄執(zhí)行l(wèi)ean join界牡。表中的所有分區(qū)都強(qiáng)制執(zhí)行鍵唯一性。
- HBASE: 管理外部 Apache HBase 表中的索引映射,是全局索引漾抬。
- INMEMORY(Flink 和 Java 的默認(rèn)值): 使用 Spark 和 Java 引擎中的內(nèi)存中 hashmap 以及 Flink 中的 Flink 內(nèi)存中狀態(tài)進(jìn)行索引宿亡。
-
BUCKET:使用桶哈希來(lái)定位包含記錄的文件組。尤其是在大規(guī)模的情況下是有利的纳令。使用
hoodie.index.bucket.engine
選擇bucket引擎類型挽荠,即如何生成bucket;-
SIMPLE(默認(rèn))
:為每個(gè)分區(qū)的文件組使用固定數(shù)量的存儲(chǔ)桶平绩,無(wú)法縮小或擴(kuò)展圈匆。這適用于 COW 和 MOR 表。由于存儲(chǔ)桶的數(shù)量無(wú)法更改馒过,并且存儲(chǔ)桶和文件組之間采用一對(duì)一映射的設(shè)計(jì)臭脓,因此該索引可能不太適合高度傾斜的分區(qū)酗钞。 -
CONSISTENT_HASHING
:支持動(dòng)態(tài)數(shù)量的存儲(chǔ)桶腹忽,并調(diào)整存儲(chǔ)桶的大小以正確調(diào)整每個(gè)存儲(chǔ)桶的大小。這解決了潛在的數(shù)據(jù)傾斜問(wèn)題砚作,即可以動(dòng)態(tài)調(diào)整具有大量數(shù)據(jù)的分區(qū)的大小以具有合理大小的多個(gè)存儲(chǔ)桶窘奏,這與 SIMPLE 存儲(chǔ)桶引擎類型中每個(gè)分區(qū)的固定數(shù)量的存儲(chǔ)桶不同。這僅適用于 MOR 表葫录。
-
- RECORD_INDEX: 將RecordKey保存到 HUDI 元數(shù)據(jù)表中的位置映射的索引着裹。記錄索引是全局索引,強(qiáng)制表中所有分區(qū)的鍵唯一性米同。支持分片以實(shí)現(xiàn)非常大的規(guī)模骇扇。
- 自定義索引: 你可以擴(kuò)展這個(gè)[publicAPI](https://github.com/apache/hudi/blob/master/hudi-client/hudi-client-common/src/main/java /org/apache/hudi/index/HoodieIndex.java) 來(lái)實(shí)現(xiàn)自定義索引。
全局索引
從上面類型看,有個(gè)GLOBAL 開頭的就是全局索引,還包括 HBase 索引
全局索引的意思是, 一個(gè)recordKey, 不管你在不在同一個(gè)分區(qū),有且只能有一個(gè);非全局是只要我分區(qū)不相同,那我就是可以同時(shí)存在多個(gè)相同的 recordKey
比如 我是一個(gè)分區(qū)表, 我有兩條數(shù)據(jù) {id:1, county:China}, {id:1, country: Janpan},順序?qū)懭? 分區(qū)為 country, recordKey 是id.
如果是全局索引,那我最后只會(huì)有一條數(shù)據(jù) {id:1, country: Janpan}
如果是非全局索引,這兩個(gè)數(shù)據(jù)都hui存在
Flink
Flink 只有三種索引: InMemory(FlinkState) 和 Bucket(SIMPLE, CONSISTENT_HASHING)
有時(shí)候我們看代碼會(huì)感到疑惑,為啥源碼里面,flink 列出了這么多索引,你卻說(shuō)只有三種,具體可以看這個(gè) PR 中的 Comment https://github.com/apache/hudi/pull/6406
雖然有個(gè) 類上面寫了這么多,根本沒(méi)有用, 最終只有 pipeline 初始化才有用.這里只有兩種方式(官方說(shuō)這個(gè)以后會(huì)報(bào)錯(cuò),如果填寫其他類型,flink 會(huì)報(bào)錯(cuò))
真正的邏輯在這里 pipelines 類,所以看 flink web UI 的時(shí)候才會(huì)出現(xiàn)這樣的情況:
桶索引沒(méi)有 bucketAssigner,有 bucketWrite 算子
如果是 Flink State index 的任務(wù) 是 stream_write, 和 bucketAssigner算子
Spark
spark 除了 InMemory 其他都支持
總結(jié)
- 什么是索引: 索引是通過(guò)HoodieKey(recordKey+partition) 與FileId 映射,從而加快查詢/更新/刪除等操作的一種機(jī)制
- 索引作用:
- 減小開銷
- upsert/delete 的基礎(chǔ)
- 加快其他特性. TimeTravel, 事務(wù),Clustering,Compaction等的基礎(chǔ).如果沒(méi)有索引,這些特性的開銷是不可接受的
- 有哪些索引
- Bloom
- InMemory(Java, Flink State)
- Bucket
- Hbase
- Simple
- Record
- 全局索引與非全局索引區(qū)別: 全局索引 相同的RecordKey 全局僅會(huì)有一個(gè),全局唯一;非全局索引,由于分區(qū),可以存在多個(gè) 相同的RecordKey,全局不唯一
- Flink 支持哪些索引: 目前僅有三種: FlinkState, Bucket(Simple, consistent_hash)