Apache Hudi Timeline:支持 ACID 事务的基础
Apache Hudi 維護(hù)在給定表上執(zhí)行的所有操作的Timeline(時間線),以支持以符合 ACID 的方式高效檢索讀取查詢的數(shù)據(jù)。 在寫入和表服務(wù)期間也會不斷查閱時間線,這是表正常運(yùn)行的關(guān)鍵。 如果任何時間線操作出現(xiàn)混亂(由于多寫入未配置鎖提供程序等),則可能導(dǎo)致數(shù)據(jù)一致性問題(數(shù)據(jù)丟失或數(shù)據(jù)重復(fù))或最終導(dǎo)致不可恢復(fù)的錯誤。 因此讓我們深入研究時間線Timeline的細(xì)微差別,以幫助操作 Apache Hudi 表。
Instant
在表格上執(zhí)行的所有操作都表示為 Hudi 時間軸中的Instant(瞬間)。 可以在表基本路徑下找到一個名為“.hoodie”的目錄,其中維護(hù)這些Instant。 Hudi instant由以下組件組成:
- Instant操作:在表上執(zhí)行的操作類型。
- Instant時間:毫秒格式的時間戳,被視為時間線上操作的標(biāo)識符。
- 狀態(tài):當(dāng)前Instant狀態(tài)。有 3 種不同的狀態(tài):Requested(請求)、Inflight(執(zhí)行)和 Completed(完成)。 給定instant將處于任何時間點(diǎn)的狀態(tài)之一。 每個操作都從"requested"狀態(tài)開始,然后移至"inflight",最后進(jìn)入 "completed" 狀態(tài),在這種情況下,整個操作被視為已完成。 在操作進(jìn)入 "completed" 狀態(tài)之前,其被視為待處理,并且不允許讀取查詢從任何此類操作中讀取任何數(shù)據(jù)。
Hudi保證在Timeline時間軸上執(zhí)行的操作是原子的并且基于Instant時間的時間軸一致。
Action
我們在 Apache Hudi 表上發(fā)生了很多不同的操作,每個操作都有不同的目的,例如由常規(guī)寫入者攝取數(shù)據(jù)、壓縮和聚簇、清理和歸檔等表服務(wù)。
對于博客的大部分內(nèi)容,我們將假設(shè)單寫入模型,因?yàn)橹攸c(diǎn)是說明時間線事件。 但如果有必要的話,也會討論一些多寫入端的場景。
Commit
Commit(提交) 操作代表寫入 COW 表。 每當(dāng)新批次被攝取到表中時,就會生成新的 CommtTime 并且操作進(jìn)入請求狀態(tài)。 可以在 Hudi 時間軸中找到"tN.commit.requested"。 例如,20230705155904980.commit.requested(其中"20230705155904980"是該操作的提交時間,請求標(biāo)志著規(guī)劃階段的完成。對于常規(guī)寫入,準(zhǔn)備階段沒有太多事情要做,執(zhí)行階段從添加"inflight"開始,一旦執(zhí)行完成,在時間軸中看到“已完成”的提交文件。
| — 20230705155904980.commit.requested
| — 20230705155904980.commit.inflight
| — 20230705155904980.commit
因此在我們看到 20230705155904980.commit 之前,所有查詢都不會讀取此提交部分寫入的任何數(shù)據(jù)。 一旦通過將 20230705155904980.commit 添加到時間線來標(biāo)記完成,任何命中表的新讀取都將讀取此感興趣的提交提交的數(shù)據(jù)。
Delta Commit
Delta Commit(增量提交)表示對 MOR 表的寫入。 這可能會產(chǎn)生日志文件或基本Parquet文件。 但"增量提交"是指定期寫入 MOR 表。 該序列類似于我們上面看到的"提交"。
| — 20230707081934362.deltacommit.requested
| — 20230707081934362.deltacommit.inflight
| — 20230707081934362.deltacommit
提交和增量提交都只會導(dǎo)致添加新文件。 完成的文件將列出有關(guān)添加的文件的所有元信息,以及寫入的字節(jié)數(shù)、寫入的記錄、更新的記錄等統(tǒng)計(jì)信息。
Clean
Hudi 在對現(xiàn)有文件組的任何更新中添加名為FileSlice(文件切片)的新版本文件。 舊版本的文件切片由Cleaner(清理器)根據(jù)清理器配置清理(或刪除)。 與常規(guī)寫入(提交和增量提交)不同,Cleaner 還將經(jīng)歷一個計(jì)劃階段,最終將導(dǎo)致 tX.clean.requested 包含清理計(jì)劃。 它將跟蹤需要在清理過程中刪除的所有文件。
將計(jì)劃序列化到請求文件中的主要原因是為了確保冪等性。 為了在清理過程中能夠防止中途崩潰,我們希望確保清理計(jì)劃一旦完成就能夠順利完成而不會失敗。 此外完成的清理準(zhǔn)確顯示了哪些文件作為清理提交的一部分被刪除,而不僅僅是部分文件列表,無論重新嘗試清理多少次。 同樣的原理也適用于聚簇計(jì)劃、壓縮計(jì)劃和恢復(fù)計(jì)劃。
| — 20230708091954360.clean.requested
| — 20230708091954360.clean.inflight
| — 20230708091954360.clean
可以在完成的"20230708091954360.clean"文件中找到有關(guān)清理器刪除的所有文件的信息。 讓我們通過一個簡單的示例來了解 Cleaner 的作用。
t1.commit:
- 插入新數(shù)據(jù)
- 添加新文件fg1_fs1(fg指文件組,fs指文件切片)
t2.commit:
- 更新同一組數(shù)據(jù)。
- 將新文件片 fg1_fs2 添加到現(xiàn)有文件組 fg1。
t3.commit:
- 更新同一組數(shù)據(jù)。
- 將新文件片 fg1_fs3 添加到現(xiàn)有文件組 fg1。
現(xiàn)在Cleaner被觸發(fā),Cleaner配置設(shè)置為“2”,以保留要保留的提交數(shù)。 因此任何早于最近 2 次提交創(chuàng)建的文件切片都會被清理。 因此 Cleaner 會將 fg1_fs1 添加到 clean 計(jì)劃中,然后在執(zhí)行過程中將其刪除。 因此存儲中僅留下 fg1_fs2 和 fg1_fs3。
t4.clean
- 清理fg1_fs1
這個循環(huán)將會重復(fù)。 例如 t5.commit 將添加 fg1_fs4,t6.clean 將刪除 fg1_fs2 等等。 可以在此處閱讀有關(guān)Cleaner的更多信息。
Replace Commit
與提交和增量提交不同,某些操作可能會導(dǎo)致替換某些數(shù)據(jù)文件。 例如,對于Clustering(聚簇),insert_overwrite 操作會添加新的數(shù)據(jù)文件,但也會替換某些數(shù)據(jù)文件。 其中大多數(shù)都是異步的,因?yàn)樘鎿Q的文件不會同步刪除,而只是標(biāo)記為替換。 在稍后的某個時間點(diǎn),由清理器負(fù)責(zé)刪除文件。
| — 20230707081954360.replacecommit.requested
| — 20230707081954360.replacecommit.inflight
| — 20230707081954360.replacecommit
比方說,使用 4 個提交 t1.commit(file1)、t2.commit(file2)、t3.commit(file3) 和 t4.commit(file4) 將 4 個數(shù)據(jù)文件寫入表中。 這里的每個文件代表Hudi中的一個不同的文件組。 假設(shè)我們觸發(fā)將小文件批處理為大文件。
t1.commit:
- 插入新數(shù)據(jù) fg1_fs1
t2.commit:
- 插入新數(shù)據(jù) fg2_fs1
t3.commit:
- 插入新數(shù)據(jù) fg3_fs1
t4.commit:
- 插入新數(shù)據(jù) fg4_fs1
t5.replacecommit 將創(chuàng)建一個新文件,file5 替換先前提交創(chuàng)建的 4 個文件。
t5.replacecommit
- 通過替換文件組(1 至 4)創(chuàng)建新文件組 fg5_fs1
在將 t5.replacecommit(已完成的時間線文件)添加到時間線之前,讀取查詢將從 4 個文件中讀取數(shù)據(jù),一旦將完成的 t5.replacecommit 添加到時間線,任何新的讀取查詢將僅讀取 file5 并忽略 file1 到 file4。 完成的 t5.replacecommit 將包含有關(guān)添加哪些文件和替換哪些文件的所有信息。
此外,Commit和Replace Commit之間的另一個區(qū)別是,常規(guī)提交的規(guī)劃階段沒有太多涉及。 但在Replace Commit情況下,規(guī)劃涉及遍歷現(xiàn)有文件組,并根據(jù)聚簇計(jì)劃策略和配置,Hudi 將確定要考慮聚簇的文件組以及如何將它們打包到不同的聚簇操作中。 因此對于非常大的表,即使是計(jì)劃也可能需要一些不小的時間。 此外在規(guī)劃階段結(jié)束時,有可能不會生成任何聚簇計(jì)劃,因此我們可能看不到任何".replacecommit.requested"文件。 這意味著此時沒有任何東西可以聚簇,并且聚簇計(jì)劃將在稍后的某個時間再次重新嘗試。 可以在此處閱讀有關(guān)聚簇的更多信息。
聚簇就是這樣的一個例子。 但還有其他操作會導(dǎo)致Replace Commit操作,其中包括insert_overwrite、insert_overwrite_table 和 delete_partition 操作。
Compaction Commit
Compaction(壓縮)是指將 MOR 表中的基礎(chǔ)文件和關(guān)聯(lián)日志文件壓縮為新的基礎(chǔ)文件的過程。 可以在此處閱讀有關(guān)壓縮的更多信息。 與聚簇類似,這也將經(jīng)歷一個規(guī)劃階段,并基于壓縮策略,可選地生成一個壓縮計(jì)劃,跟蹤日志文件列表和要壓縮的基本文件。 如果生成了計(jì)劃,它將在時間線中生成一個compaction.requested 文件。 這標(biāo)志著規(guī)劃階段的結(jié)束。 然后在執(zhí)行階段,將創(chuàng)建一個inflight文件,最終一旦壓縮完成,一個完成的文件將被添加到時間線中以標(biāo)記感興趣的壓縮的完成。
| — 20230707091954370.compaction.requested
| — 20230707091954370.compaction.inflight
| — 20230707091954370.commit
同樣與 Clean 和 Clustering 類似,計(jì)劃一旦序列化(換句話說,一旦requested文件寫出),Hudi 就可以適應(yīng)任意數(shù)量的崩潰和重新嘗試,最終 Hudi 一定會完成它,確保所有部分失敗的嘗試都得到正確清理,并且只有最終成功嘗試的數(shù)據(jù)文件完好無損。 當(dāng)操作一個非常大的表并且必須壓縮大量文件組時,這一點(diǎn)非常關(guān)鍵。 此外假設(shè)計(jì)劃的壓縮最終完成,表中的其他操作也將繼續(xù)進(jìn)行。 因此我們永遠(yuǎn)無法恢復(fù)計(jì)劃的壓縮。 如果表中有更多寫入端,則必須不惜一切代價(jià)完成它,這是Hudi支持異步壓縮的關(guān)鍵設(shè)計(jì)之一。 如果看到具有以下序列的時間線,則它是有效的事件序列。
| — t100.compaction.requested
| — t110.deltacommit.requested
| — t110.deltacommit.inflight
| — t100.compaction.inflight
| — t110.deltacommit
| — t100.commit
如果在連續(xù)模式下使用 Deltastreamer,這是通常看到的時間線事件序列。
Rollback
使用Rollback(回滾)操作回滾任何部分失敗的寫入。 在單寫入端模式下,回滾是急切的,即每當(dāng)開始新的提交時,Hudi 都會檢查任何待處理的提交并觸發(fā)回滾。 在Hudi支持的所有不同操作中,只有Clean、Rollback和Restore會刪除文件,其他操作都不會刪除任何數(shù)據(jù)文件,Replace Commit可以將某些文件標(biāo)記為已替換,但不會刪除它們。
回滾計(jì)劃階段包括查找作為部分失敗提交的一部分添加的所有文件并將其添加到回滾計(jì)劃中。正如我們之前所看到的,計(jì)劃被序列化到 rollback.requested 文件中。 執(zhí)行首先在時間線中創(chuàng)建一個運(yùn)行中的文件,最終當(dāng)回滾完成時,完成的回滾文件將被添加到時間線中。
假設(shè)這是崩潰之前的時間線。
| — t10.commit.requestet
| — t10.commit.inflight
| — t10.commit
| — t20.commit.requested
| — t20.commit.inflight
就在這之后,進(jìn)程崩潰了。 因此用戶重新啟動管道并將觸發(fā)回滾,因?yàn)?t20 被推斷為待處理。
| — t10.commit.requested
| — t10.commit.inflight
| — t10.commit
| — t20.commit.requested
| — t20.commit.inflight
| — t25.rollback.requested
回滾結(jié)束時,Hudi 會刪除正在回滾的提交的提交元文件。 在這種情況下,與提交 t20 相關(guān)的所有時間線文件都將被刪除。 因此回滾完成后的時間線可能如下所示。
| — t10.commit.requested
| — t10.commit.inflight
| — t10.commit
| — t25.rollback.requested。
| — t25.rollback.inflight
| — t25.rollback
對于多寫入端,Hudi 還引入了延遲回滾,即它使用基于心跳的回滾機(jī)制,我們會在未來的博客中更深入地了解回滾算法。
與聚簇、壓縮類似,回滾也被設(shè)計(jì)成冪等的。我們在請求文件中序列化計(jì)劃,因此即使回滾中途崩潰,我們也可以重新嘗試,不會出現(xiàn)任何問題。 Hudi 確保重復(fù)使用相同的回滾即時時間來回滾給定的提交。 完成的回滾文件將列出在回滾過程中刪除的所有文件。 COW中的回滾將刪除部分寫入的文件,但在MOR的情況下,如果部分失敗的提交添加了一個日志文件,則回滾將添加另一個帶有回滾塊的日志文件,并且不會刪除原始日志文件。 這是 MOR 表的關(guān)鍵設(shè)計(jì)之一,以將任何寫入保留為追加。 我們還可以在以后的一些博客中查看日志文件設(shè)計(jì)。
Savepoint
為了在災(zāi)難和恢復(fù)場景中提供幫助,Hudi 引入了兩種操作,稱為Savepoint(保存點(diǎn))和Restore(恢復(fù))。 將保存點(diǎn)添加到提交可確保清理和歸檔不會觸及與保存點(diǎn)提交相關(guān)的任何內(nèi)容。 這意味著用戶可以根據(jù)需要將表恢復(fù)到感興趣的保存點(diǎn)提交。 僅當(dāng)保存點(diǎn)尚未清理時才允許將其添加到提交中。
Savepoint 只有兩種狀態(tài):正在運(yùn)行和已完成。 由于沒有計(jì)劃階段,因此沒有保存點(diǎn)請求。 在執(zhí)行階段,Hudi 會查找截至感興趣的提交時間提供讀取查詢所需的所有文件。 這些文件將添加到 tX.savepoint.inflight 文件中。 并立即將完整的保存點(diǎn)文件添加到時間線中。
| — t10.commit.requested
| — t10.commit.inflight
| — t10.commit
| — t10.savepoint.inflight
| — t10.savepoint
也可以在稍后階段添加保存點(diǎn),只是清理程序不應(yīng)該清理文件。 例如表可能有從 t10 到 200 的提交(每 10 秒一次)。 因此在時間 t210,如果 Cleaner 清理 t30 之前的數(shù)據(jù)文件,則允許為t50添加保存點(diǎn)。
Restore
Restore(恢復(fù))用于將整個表恢復(fù)到某個較舊的時間點(diǎn)。 萬一表中出現(xiàn)了一些壞數(shù)據(jù),或者數(shù)據(jù)損壞或其他正當(dāng)原因,如果用戶希望將表恢復(fù)到 10 小時前的狀態(tài),恢復(fù)操作就會派上用場。 用戶可以將保存點(diǎn)添加到 10 小時前的提交之一并觸發(fā)恢復(fù)。 從技術(shù)上講,恢復(fù)意味著按時間倒序回滾 N 個提交。 例如如果表有提交 t10、t20、t30、t40、t50、t60、t70、t80、t90 和 t100。 用戶更愿意將表恢復(fù)到 t40。 Hudi 將回滾 t100,然后回滾 t90,然后回滾 t80,依此類推。直到 t50 回滾開始。
Hudi 將像其他表服務(wù)一樣經(jīng)歷類似的狀態(tài)轉(zhuǎn)換。 將生成請求的計(jì)劃來跟蹤需要回滾的所有提交,然后在執(zhí)行過程中,將創(chuàng)建一個運(yùn)行中的文件,最終完成后,完整的恢復(fù)文件將添加到時間線中。
| — t10.commit.requested
| — t10.commit.inflight
| — t10.commit
| — t10.savepoint.inflight
| — t10.savepoint
| — t20.commit.requested
| — t20.commit.inflight
| — t20.commit
| - ..
| - ..
| — t100.commit.requested
| — t100.commit.inflight
| — t100.commit
恢復(fù)后時間線可能如下所示
| — t10.commit.requested
| — t10.commit.inflight
| — t10.commit
| — t10.savepoint.inflight
| — t10.savepoint
| — t120.restore.requested
| — t120.restore.inflight|
| — t120.restore
Index
Hudi支持添加各種索引來輔助讀寫延遲,此類分區(qū)包括列統(tǒng)計(jì)分區(qū)和布隆過濾器分區(qū),要首次為大型表初始化這些索引,我們不能阻止攝取寫入器,因?yàn)樗赡軙加么罅繒r間。 因此 Hudi 引入了 AsyncIndexer 來協(xié)助異步初始化這些分區(qū)。
| — t200.indexing.requested
| — t200.indexing.inflight
| — t200.indexing
與任何其他操作一樣,這會經(jīng)歷典型的狀態(tài)轉(zhuǎn)換,我們將在單獨(dú)的博客中詳細(xì)介紹異步索引。
Active/Archive Timeline
Hudi 將整個時間線剖析為Active Timeline(活動時間線)和Archive Timeline(存檔時間線)。 在".hoodie"目錄下看到的任何Instant均指活動時間線,而存檔的那些Instant將進(jìn)入".hoodie/archived"目錄。 可以在此處閱讀有關(guān)存檔時間表的更多信息。區(qū)分Ative/Archive Timeline背后的基本原理是確保我們對元數(shù)據(jù)(時間線)有最大限制,防止隨著時間線越來越長,讀取出現(xiàn)延遲增加的情況。
Hudi CLI
Hudi CLI 有查看表時間線的命令。我們將在其他一些博客中通過示例詳細(xì)介紹它們,如果想嘗試一下,命令是"timeline"。
結(jié)論
時間線在提供符合 ACID 的正確數(shù)據(jù)方面發(fā)揮著非常重要的作用。 了解不同的時間線事件對于管理任何組織中的 Apache Hudi 表都非常有益,并且還有助于根據(jù)需要進(jìn)行問題排查。
總結(jié)
以上是生活随笔為你收集整理的Apache Hudi Timeline:支持 ACID 事务的基础的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Kubernetes:kube-apis
- 下一篇: 抽动症孩子的饮食?