醍醐灌顶 | 我们谈论的Exactly once到底是什么?
點擊上方“朱小廝的博客”,選擇“設為星標”
后臺回復"書",獲取
后臺回復“k8s”,可領取k8s資料
轉自:https://juejin.cn/post/6844903857558913038
英文原文:https://streaml.io/blog/exactly-once
分布式事件流處理正逐漸成為大數據領域中一個熱門話題。著名的流處理引擎(Streaming Processing Engines, SPEs)包括Apache Storm、Apache Flink、Heron、Apache Kafka(Kafka Streams)以及Apache Spark(Spark Streaming)。流處理引擎中一個著名的且經常被廣泛討論的特征是它們的處理語義,而“exactly-once”是其中最受歡迎的,同時也有很多引擎聲稱它們提供“exactly-once”處理語義。
然而,圍繞著“exactly-once”究竟是什么、它牽扯到什么以及當流處理引擎聲稱提供“exactly-once”語義時它究竟意味著什么,仍然存在著很多誤解與歧義。而用來描述處理語義的“exactly-once”這一標簽同樣也是非常誤導人的。在這篇博文當中,我將會討論眾多受歡迎的引擎所提供的“exactly-once”語義間的不同之處,以及為什么“exactly-once”更好的描述是“effective-once”。我還會討論用來實現“exactly-once”的常用技術間的權衡(tradeoffs)
背景
流處理(streaming process),有時也被稱為事件處理(event processing),可以被簡潔地描述為對于一個無限的數據或事件序列的連續處理。一個流,或事件,處理應用可以或多或少地由一個有向圖,通常是一個有向無環圖(DAG),來表達。在這樣一個圖中,每條邊表示一個數據或事件流,而每個頂點表示使用應用定義好的邏輯來處理來自相鄰邊的數據或事件的算子。其中有兩種特殊的頂點,通常被稱作sources與sinks。Sources消費外部數據/事件并將其注入到應用當中,而sinks通常收集由應用產生的結果。圖1描述了一個流處理應用的例子。
A typical Heron processing topology
一個執行流/事件處理應用的流處理引擎通常允許用戶制定一個可靠性模式或者處理語義,來標示引擎會為應用圖的實體之間的數據處理提供什么樣的保證。由于你總是會遇到網絡、機器這些會導致數據丟失的故障,因而這些保證是有意義的。有三種模型/標簽,at-most-once、at-least-once以及exactly-once,通常被用來描述流處理引擎應該為應用提供的數據處理語義。接下來是對這些不同的處理語義的寬泛的定義:
At-most-once
這實質上是一個“盡力而為”(best effort)的方法。數據或者事件被保證只會被應用中的所有算子最多處理一次。這就意味著對于流處理應用完全處理它之前丟失的數據,也不會有額外的重試或重傳嘗試。圖2展示了一個相關的例子:
At-most-once processing semantics
At-least-once
數據或事件被保證會被應用圖中的所有算子都至少處理一次。這通常意味著當事件在被應用完全處理之前就丟失的話,其會被從source開始重放(replayed)或重傳(retransmitted)。由于事件會被重傳,那么一個事件有時就會被處理超過一次,也就是所謂的at-least-once。圖3展示了一個at-least-once的例子。在這一示例中,第一個算子第一次處理一個事件時失敗,之后在重試時成功,并在結果證明沒有必要的第二次重試時成功。
At-least-once processing semantics
Exactly-once
倘若發生各種故障,事件也會被確保只會被流應用中的所有算子“恰好”處理一次。拿來實現“exactly-once”的有兩種受歡迎的典型機制:1. 分布式快照/狀態檢查點(checkpointing) 2. At-least-once的事件投遞加上消息去重
用來實現“exactly-once”的分布式快照/狀態檢查點方法是受到了Chandy-Lamport分布式快照算法1的啟發。在這種機制中,流處理應用中的每一個算子的所有狀態都會周期性地checkpointed。倘若系統發生了故障,每一個算子的所有狀態都會回滾到最近的全局一致的檢查點處。在回滾過程中,所有的處理都會暫停。Sources也會根據最近的檢查點重置到正確到offset。整個流處理應用基本上倒回到最近的一致性狀態,處理也可以從這個狀態重新開始。圖4展示了這種機制的基本原理。
Distributed snapshot
在圖4中,流處理應用T1時在正常地工作,同時狀態也被checkpointed。T2時,算子處理一個輸入數據時失敗了。此時,S = 4的狀態已經保存到了持久化存儲當中,而S = 12的狀態仍然位于算子的內存當中。為了解決這個不一致,T3時processing graph倒回到S = 4的狀態并“重放”流中的每一個狀態直到最新的狀態,并處理每一個數據。最終結果是雖然某些數據被處理了多次,但是無論執行了多少次回滾,結果狀態依然是相同的。用來實現“exactly-once”的另一種方法是在每一個算子的基礎上,將at-least-once的事件投遞與事件去重相結合。使用這種方法的引擎會重放失敗的事件以進一步嘗試進行處理,并在每一個算子上,在事件進入到用戶定義的邏輯之前刪除重復的事件。這一機制需要為每一個算子維護一份事務日志(transaction log)來記錄哪些事件已經處理過了。使用類似這一機制的引擎有Google的MillWheel2與Apache Kafka Streams。圖5展示了這一機制的重點。
At-least-once delivery plus deduplication
exactly-once確實是exactly-once嗎?
現在讓我們重新審視『精確一次』處理語義真正對最終用戶的保證?!壕_一次』這個術語在描述正好處理一次時會讓人產生誤導。
有些人可能認為『精確一次』描述了事件處理的保證,其中流中的每個事件只被處理一次。實際上,沒有引擎能夠保證正好只處理一次。在面對任意故障時,不可能保證每個算子中的用戶定義邏輯在每個事件中只執行一次,因為用戶代碼被部分執行的可能性是永遠存在的。
考慮具有流處理運算符的場景,該運算符執行打印傳入事件的 ID 的映射操作,然后返回事件不變。下面的偽代碼說明了這個操作:
每個事件都有一個 GUID (全局惟一ID)。如果用戶邏輯的精確執行一次得到保證,那么事件 ID 將只輸出一次。但是,這是無法保證的,因為在用戶定義的邏輯的執行過程中,隨時都可能發生故障。引擎無法自行確定執行用戶定義的處理邏輯的時間點。因此,不能保證任意用戶定義的邏輯只執行一次。這也意味著,在用戶定義的邏輯中實現的外部操作(如寫數據庫)也不能保證只執行一次。此類操作仍然需要以冪等的方式執行。
那么,當引擎聲明『精確一次』處理語義時,它們能保證什么呢?如果不能保證用戶邏輯只執行一次,那么什么邏輯只執行一次?當引擎聲明『精確一次』處理語義時,它們實際上是在說,它們可以保證引擎管理的狀態更新只提交一次到持久的后端存儲。
上面描述的兩種機制都使用持久的后端存儲作為真實性的來源,可以保存每個算子的狀態并自動向其提交更新。對于機制 1 (分布式快照 / 狀態檢查點),此持久后端狀態用于保存流應用程序的全局一致狀態檢查點(每個算子的檢查點狀態)。對于機制 2 (至少一次事件傳遞加上重復數據刪除),持久后端狀態用于存儲每個算子的狀態以及每個算子的事務日志,該日志跟蹤它已經完全處理的所有事件。
提交狀態或對作為真實來源的持久后端應用更新可以被描述為恰好發生一次。然而,如上所述,計算狀態的更新 / 更改,即處理在事件上執行任意用戶定義邏輯的事件,如果發生故障,則可能不止一次地發生。換句話說,事件的處理可以發生多次,但是該處理的效果只在持久后端狀態存儲中反映一次。因此,我們認為有效地描述這些處理語義最好的術語是『有效一次』(effectively once)。
那么,當引擎聲明『精確一次』處理語義時,它們能保證什么呢?如果不能保證用戶邏輯只執行一次,那么什么邏輯只執行一次?當引擎聲明『精確一次』處理語義時,它們實際上是在說,它們可以保證引擎管理的狀態更新只提交一次到持久的后端存儲
分布式快照 vs at-least-once
從語義的角度來看,分布式快照和至少一次事件傳遞以及重復數據刪除機制都提供了相同的保證。然而,由于兩種機制之間的實現差異,存在顯著的性能差異。
機制 1(分布式快照 / 狀態檢查點)的性能開銷是最小的,因為引擎實際上是往流應用程序中的所有算子一起發送常規事件和特殊事件,而狀態檢查點可以在后臺異步執行。但是,對于大型流應用程序,故障可能會更頻繁地發生,導致引擎需要暫停應用程序并回滾所有算子的狀態,這反過來又會影響性能。流式應用程序越大,故障發生的可能性就越大,因此也越頻繁,反過來,流式應用程序的性能受到的影響也就越大。然而,這種機制是非侵入性的,運行時需要的額外資源影響很小。
機制 2(至少一次事件傳遞加重復數據刪除)可能需要更多資源,尤其是存儲。使用此機制,引擎需要能夠跟蹤每個算子實例已完全處理的每個元組,以執行重復數據刪除,以及為每個事件執行重復數據刪除本身。這意味著需要跟蹤大量的數據,尤其是在流應用程序很大或者有許多應用程序在運行的情況下。執行重復數據刪除的每個算子上的每個事件都會產生性能開銷。但是,使用這種機制,流應用程序的性能不太可能受到應用程序大小的影響。對于機制 1,如果任何算子發生故障,則需要發生全局暫停和狀態回滾;對于機制 2,失敗的影響更加局部性。當在算子中發生故障時,可能尚未完全處理的事件僅從上游源重放/重傳。性能影響與流應用程序中發生故障的位置是隔離的,并且對流應用程序中其他算子的性能幾乎沒有影響。從性能角度來看,這兩種機制的優缺點如下。
分布式快照 / 狀態檢查點的優缺點:
優點:
較小的性能和資源開銷缺點:
對性能的影響較大
拓撲越大,對性能的潛在影響越大
至少一次事件傳遞以及重復數據刪除機制的優缺點:
優點:
故障對性能的影響是局部的
故障的影響不一定會隨著拓撲的大小而增加缺點:
可能需要大量的存儲和基礎設施來支持
每個算子的每個事件的性能開銷
雖然從理論上講,分布式快照和至少一次事件傳遞加重復數據刪除機制之間存在差異,但兩者都可以簡化為至少一次處理加冪等性。對于這兩種機制,當發生故障時(至少實現一次),事件將被重放/重傳,并且通過狀態回滾或事件重復數據刪除,算子在更新內部管理狀態時本質上是冪等的。
總結
在這篇博客文章中,我希望能夠讓你相信『精確一次』這個詞是非常具有誤導性的。提供『精確一次』的處理語義實際上意味著流處理引擎管理的算子狀態的不同更新只反映一次?!壕_一次』并不能保證事件的處理,即任意用戶定義邏輯的執行,只會發生一次。我們更喜歡用『有效一次』(effectively once)這個術語來表示這種保證,因為處理不一定保證只發生一次,但是對引擎管理的狀態的影響只反映一次。兩種流行的機制,分布式快照和重復數據刪除,被用來實現精確/有效的一次性處理語義。這兩種機制為消息處理和狀態更新提供了相同的語義保證,但是在性能上存在差異。這篇文章并不是要讓你相信任何一種機制都優于另一種,因為它們各有利弊。
想知道更多?掃描下面的二維碼關注我
后臺回復"技術",加入技術群
后臺回復“k8s”,可領取k8s資料
【精彩推薦】
原創|OpenAPI標準規范
中臺不是萬能藥,關于中臺的思考和嘗試
ClickHouse到底是什么?為什么如此牛逼!
原來ElasticSearch還可以這么理解
面試官:InnoDB中一棵B+樹可以存放多少行數據?
微服務下如何解耦?對于已經緊耦合下如何重構?
如何構建一套高性能、高可用、低成本的視頻處理系統?
架構之道:分離業務邏輯和技術細節
星巴克不使用兩階段提交
點個贊+在看,少個 bug?????
總結
以上是生活随笔為你收集整理的醍醐灌顶 | 我们谈论的Exactly once到底是什么?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JAVA字节码指令iload_n为什么只
- 下一篇: 清晰!我们从来都反对“大中台,小前台”的