點(diǎn)擊上方藍(lán)色字體,選擇“設(shè)為星標(biāo)”
點(diǎn)擊右側(cè)關(guān)注,大數(shù)據(jù)開發(fā)領(lǐng)域最強(qiáng)公眾號(hào)!點(diǎn)擊右側(cè)關(guān)注,暴走大數(shù)據(jù)!
Apache Hudi是一個(gè)Data Lakes的開源方案,Hudi是Hadoop Updates and Incrementals的簡(jiǎn)寫,它是由Uber開發(fā)并開源的Data Lakes解決方案。Hudi具有如下基本特性/能力:Hudi能夠攝入(Ingest)和管理(Manage)基于HDFS之上的大型分析數(shù)據(jù)集,主要目的是高效的減少入庫(kù)延時(shí)。
Hudi基于Spark來(lái)對(duì)HDFS上的數(shù)據(jù)進(jìn)行更新、插入、刪除等。
Hudi在HDFS數(shù)據(jù)集上提供如下流原語(yǔ):插入更新(如何改變數(shù)據(jù)集);增量拉?。ㄈ绾潍@取變更的數(shù)據(jù))。
Hudi可以對(duì)HDFS上的parquet格式數(shù)據(jù)進(jìn)行插入/更新操作。
Hudi通過(guò)自定義InputFormat與Hadoop生態(tài)系統(tǒng)(Spark、Hive、Parquet)集成。
Hudi通過(guò)Savepoint來(lái)實(shí)現(xiàn)數(shù)據(jù)恢復(fù)。
目前,Hudi支持Spark 2.x版本,建議使用2.4.4+版本的Spark。
與Kudu相比,Kudu是一個(gè)支持OLTP workload的數(shù)據(jù)存儲(chǔ)系統(tǒng),而Hudi的設(shè)計(jì)目標(biāo)是基于Hadoop兼容的文件系統(tǒng)(如HDFS、S3等),重度依賴Spark的數(shù)據(jù)處理能力來(lái)實(shí)現(xiàn)增量處理和豐富的查詢能力,Hudi支持Incremental Pulling而Kudu不支持。
Hudi能夠整合Batch和Streaming處理的能力,這是通過(guò)利用Spark自身支持的基本能力來(lái)實(shí)現(xiàn)的。一個(gè)數(shù)據(jù)處理Pipeline通常由Source、Processing、Sink三個(gè)部分組成,Hudi可以作為Source、Sink,它把數(shù)據(jù)存儲(chǔ)到分布式文件系統(tǒng)(如HDFS)中。
Apache Hudi在大數(shù)據(jù)應(yīng)用場(chǎng)景中,所處的位置,如下圖所示:
從上圖中可見,Hudi能夠與Hive、Spark、Presto這類處理引擎一起工作。Hudi有自己的數(shù)據(jù)表,通過(guò)將Hudi的Bundle整合進(jìn)Hive、Spark、Presto等這類引擎中,使得這些引擎可以查詢Hudi表數(shù)據(jù),從而具備Hudi所提供的Snapshot Query、Incremental Query、Read Optimized Query的能力。
下面,先從Apache Hudi中提出的幾個(gè)概念開始,來(lái)了解Hudi的設(shè)計(jì):Hudi內(nèi)部對(duì)每個(gè)表都維護(hù)了一個(gè)Timeline,這個(gè)Timeline是由一組作用在某個(gè)表上的Instant對(duì)象組成。Instant表示在某個(gè)時(shí)間點(diǎn)對(duì)表進(jìn)行操作的,從而達(dá)到某一個(gè)狀態(tài)的表示,所以Instant包含Instant Action,Instant Time和Instant State這三個(gè)內(nèi)容,它們的含義如下所示:Instant Action:對(duì)Hudi表執(zhí)行的操作類型,目前包括COMMITS、CLEANS、DELTA_COMMIT、COMPACTION、ROLLBACK、SAVEPOINT這6種操作類型。
Instant Time:表示一個(gè)時(shí)間戳,這個(gè)時(shí)間戳必須是按照Instant Action開始執(zhí)行的時(shí)間順序單調(diào)遞增的。
Instant State:表示在指定的時(shí)間點(diǎn)(Instant Time)對(duì)Hudi表執(zhí)行操作(Instant Action)后,表所處的狀態(tài),目前包括REQUESTED(已調(diào)度但未初始化)、INFLIGHT(當(dāng)前正在執(zhí)行)、COMPLETED(操作執(zhí)行完成)這3種狀態(tài)。
下面,根據(jù)官網(wǎng)給出的一個(gè)例子來(lái)理解一下Timeline,如下圖所示:
例子場(chǎng)景是,在10:00~10.20之間,要對(duì)一個(gè)Hudi表執(zhí)行Upsert操作,操作的頻率大約是5分鐘執(zhí)行一次。
每次操作執(zhí)行完成,會(huì)看到對(duì)應(yīng)這個(gè)Hudi表的Timeline上,有一系列的COMMIT元數(shù)據(jù)生成。
當(dāng)滿足一定條件時(shí),會(huì)在指定的時(shí)刻對(duì)這些COMMIT進(jìn)行CLEANS和COMPACTION操作,這兩個(gè)操作都是在后臺(tái)完成,其中在10:05之后執(zhí)行了一次CLEANS操作,10:10之后執(zhí)行了一次COMPACTION操作。
我們看到,從數(shù)據(jù)生成到最終到達(dá)Hudi系統(tǒng),可能存在延遲,如圖中數(shù)據(jù)大約在07:00、08:00、09:00時(shí)生成,數(shù)據(jù)到達(dá)大約延遲了分別3、2、1小時(shí)多,最終生成COMMIT的時(shí)間才是Upsert的時(shí)間。對(duì)于數(shù)據(jù)到達(dá)時(shí)間(Arrival Time)和事件時(shí)間(Event Time)相關(guān)的數(shù)據(jù)延遲性(Latency)和完整性(Completeness)的權(quán)衡,Hudi可以將數(shù)據(jù)Upsert到更早時(shí)間的Buckets或Folders下面。通過(guò)使用Timeline來(lái)管理,當(dāng)增量查詢10:00之后的最新數(shù)據(jù)時(shí),可以非常高效的找到10:00之后發(fā)生過(guò)更新的文件,而不必根據(jù)延遲時(shí)間再去掃描更早時(shí)間的文件,比如這里,就不需要掃描7:00、8:00或9:00這些時(shí)刻對(duì)應(yīng)的文件(Buckets)。Hudi將表組織成HDFS上某個(gè)指定目錄(basepath)下的目錄結(jié)構(gòu),表被分成多個(gè)分區(qū),分區(qū)是以目錄的形式存在,每個(gè)目錄下面會(huì)存在屬于該分區(qū)的多個(gè)文件,類似Hive表,每個(gè)Hudi表分區(qū)通過(guò)一個(gè)分區(qū)路徑(partitionpath)來(lái)唯一標(biāo)識(shí)。在每個(gè)分區(qū)下面,通過(guò)文件分組(File Group)的方式來(lái)組織,每個(gè)分組對(duì)應(yīng)一個(gè)唯一的文件ID。每個(gè)文件分組中包含多個(gè)文件分片(File Slice),每個(gè)文件分片包含一個(gè)Base文件(*.parquet),這個(gè)文件是在執(zhí)行COMMIT/COMPACTION操作的時(shí)候生成的,同時(shí)還生成了幾個(gè)日志文件(*.log.*),日志文件中包含了從該Base文件生成以后執(zhí)行的插入/更新操作。
Hudi采用MVCC設(shè)計(jì),當(dāng)執(zhí)行COMPACTION操作時(shí),會(huì)合并日志文件和Base文件,生成新的文件分片。CLEANS操作會(huì)清理掉不用的/舊的文件分片,釋放存儲(chǔ)空間。
Hudi會(huì)通過(guò)記錄Key與分區(qū)Path組成Hoodie Key,即Record Key+Partition Path,通過(guò)將Hoodie Key映射到前面提到的文件ID,具體其實(shí)是映射到file_group/file_id,這就是Hudi的索引。一旦記錄的第一個(gè)版本被寫入文件中,對(duì)應(yīng)的Hoodie Key就不會(huì)再改變了。使用專門的列式文件格式存儲(chǔ)數(shù)據(jù),例如Parquet格式。更新時(shí)保存多版本,并且在寫的過(guò)程中通過(guò)異步的Merge來(lái)實(shí)現(xiàn)重寫(Rewrite)數(shù)據(jù)文件。
Copy-On-Write表只包含列式格式的Base文件,每次執(zhí)行COMMIT操作會(huì)生成新版本的Base文件,最終執(zhí)行COMPACTION操作時(shí)還是會(huì)生成列式格式的Base文件。所以,Copy-On-Write表存在寫放大的問(wèn)題,因?yàn)槊看斡懈虏僮鞫紩?huì)重寫(Rewrite)整個(gè)Base文件。
通過(guò)官網(wǎng)給出的一個(gè)例子,來(lái)說(shuō)明寫入Copy-On-Write表,并進(jìn)行查詢操作的基本流程,如下圖所示:
上圖中,每次執(zhí)行INSERT或UPDATE操作,都會(huì)在Timeline上生成一個(gè)的COMMIT,同時(shí)對(duì)應(yīng)著一個(gè)文件分片(File Slice)。如果是INSERT操作則生成文件分組的第一個(gè)新的文件分片,如果是UPDATE操作則會(huì)生成一個(gè)新版本的文件分片。
寫入過(guò)程中可以進(jìn)行查詢,如果查詢COMMIT為10:10之前的數(shù)據(jù),則會(huì)首先查詢Timeline上最新的COMMIT,通過(guò)過(guò)濾掉只會(huì)小于10:10的數(shù)據(jù)查詢出來(lái),即把文件ID為1、2、3且版本為10:05的文件分片查詢出來(lái)。
使用列式和行式文件格式混合的方式來(lái)存儲(chǔ)數(shù)據(jù),列式文件格式比如Parquet,行式文件格式比如Avro。更新時(shí)寫入到增量(Delta)文件中,之后通過(guò)同步或異步的COMPACTION操作,生成新版本的列式格式文件。
Merge-On-Read表存在列式格式的Base文件,也存在行式格式的增量(Delta)文件,新到達(dá)的更新都會(huì)寫到增量日志文件中,根據(jù)實(shí)際情況進(jìn)行COMPACTION操作來(lái)將增量文件合并到Base文件上。通常,需要有效的控制增量日志文件的大小,來(lái)平衡讀放大和寫放大的影響。
Merge-On-Read表可以支持Snapshot Query和Read Optimized Query,下面的例子展示了Merge-On-Read表讀寫的基本流程,如下圖所示:
上圖中,每個(gè)文件分組都對(duì)應(yīng)一個(gè)增量日志文件(Delta Log File)。COMPACTION操作在后臺(tái)定時(shí)執(zhí)行,會(huì)把對(duì)應(yīng)的增量日志文件合并到文件分組的Base文件中,生成新版本的Base文件。
對(duì)于查詢10:10之后的數(shù)據(jù)的Read Optimized Query,只能查詢到10:05及其之前的數(shù)據(jù),看不到之后的數(shù)據(jù),查詢結(jié)果只包含版本為10:05、文件ID為1、2、3的文件;但是Snapshot Query是可以查詢到10:05之后的數(shù)據(jù)的。
只能查詢到給定COMMIT或COMPACTION后的最新快照數(shù)據(jù)。對(duì)于Copy-On-Write表,Snapshot Query能夠查詢到,已經(jīng)存在的列式格式文件(Parquet文件);對(duì)于Merge-On-Read表,Snapshot Query能夠查詢到,通過(guò)合并已存在的Base文件和增量日志文件得到的數(shù)據(jù)。只能查詢到最新寫入Hudi表的數(shù)據(jù),也就是給定的COMMIT/COMPACTION之后的最新數(shù)據(jù)。只能查詢到給定的COMMIT/COMPACTION之前所限定范圍的最新數(shù)據(jù)。也就是說(shuō),只能看到列式格式Base文件中的最新數(shù)據(jù)。基于Hudi表和Hudi Bundle,外部的其他查詢引擎可以非常方便的查詢Hudi表,比如Hive、Spark SQL、Presto等。Hudi支持在Copy-On-Write表和Merge-On-Read表兩種類型的表,同時(shí)支持基于Hudi表的Snapshot Query、Incremental Query、Read Optimized Query的能力。下面是Hudi支持的外部查詢引擎支持查詢的能力矩陣:版權(quán)聲明:
本文為大數(shù)據(jù)技術(shù)與架構(gòu)整理,原作者獨(dú)家授權(quán)。未經(jīng)原作者允許轉(zhuǎn)載追究侵權(quán)責(zé)任。微信公眾號(hào)|import_bigdata
歡迎點(diǎn)贊+收藏+轉(zhuǎn)發(fā)朋友圈素質(zhì)三連文章不錯(cuò)?點(diǎn)個(gè)【在看】吧!??