談談流計算中的『Exactly Once』特性

做者:寶牛算法

本文翻譯自 streaml.io 網站上的一篇博文:「Exactly once is NOT exactly the same」 ,分析了流計算系統中常說的『Exactly Once』特性,主要觀點是:『精確一次』並不保證是徹底同樣。主要內容以下:數據庫

  1. 背景
  • 1.1. 最多一次(At-most-once)
  • 1.2. 至少一次(At-least-once)
  • 1.3. 精確一次(Exactly-once)
  1. 『精確一次』是真正的『精確一次』嗎?
  2. 分佈式快照與至少一次事件傳遞和重複數據刪除的比較
  3. 結論
  4. 參考

目前市面上使用較多的流計算系統有 Apache Storm,Apache Flink, Heron, Apache Kafka (Kafka Streams) 和 Apache Spark (Spark Streaming)。關於流計算系統有個被普遍討論的特性是『exactly-once』語義,不少系統宣稱已經支持了這一特性。可是,到底什麼是『exactly-once』,怎麼樣纔算是實現了『exactly-once』,人們存在不少誤解和歧義。接下來咱們作下分析。後端

1、背景

流處理(有時稱爲事件處理)能夠簡單地描述爲是對無界數據或事件的連續處理。流或事件處理應用程序能夠或多或少地被描述爲有向圖,而且一般被描述爲有向無環圖(DAG)。在這樣的圖中,每一個邊表示數據或事件流,每一個頂點表示運算符,會使用程序中定義的邏輯處理來自相鄰邊的數據或事件。有兩種特殊類型的頂點,一般稱爲 sources 和 sinks。sources讀取外部數據/事件到應用程序中,而 sinks 一般會收集應用程序生成的結果。下圖是流式應用程序的示例。網絡

A typical stream processing topology異步

流處理引擎一般容許用戶指定可靠性模式或處理語義,以指示它將爲整個應用程序中的數據處理提供哪些保證。這些保證是有意義的,由於你始終會遇到因爲網絡,機器等可能致使數據丟失的故障。流處理引擎一般爲應用程序提供了三種數據處理語義:最多一次、至少一次和精確一次。分佈式

以下是對這些不一樣處理語義的寬鬆定義:性能

最多一次(At-most-once)

這本質上是一『盡力而爲』的方法。保證數據或事件最多由應用程序中的全部算子處理一次。 這意味着若是數據在被流應用程序徹底處理以前發生丟失,則不會進行其餘重試或者從新發送。下圖中的例子說明了這種狀況。網站

At-most-once processing semanticsspa

至少一次(At-least-once)

應用程序中的全部算子都保證數據或事件至少被處理一次。這一般意味着若是事件在流應用程序徹底處理以前丟失,則將從源頭重放或從新傳輸事件。然而,因爲事件是能夠被重傳的,所以一個事件有時會被處理屢次,這就是所謂的至少一次。翻譯

下圖的例子描述了這種狀況:第一個算子最初未能成功處理事件,而後在重試時成功,接着在第二次重試時也成功了,實際上是沒有必要的。

At-least-once processing semantics

精確一次(Exactly-once)

即便是在各類故障的狀況下,流應用程序中的全部算子都保證事件只會被『精確一次』的處理。(也有文章將 Exactly-once 翻譯爲:徹底一次,剛好一次)

一般使用兩種流行的機制來實現『精確一次』處理語義。

  • 分佈式快照 / 狀態檢查點
  • 至少一次事件傳遞和對重複數據去重

實現『精確一次』的分佈式快照/狀態檢查點方法受到 Chandy-Lamport 分佈式快照算法的啓發[1]。經過這種機制,流應用程序中每一個算子的全部狀態都會按期作 checkpoint。若是是在系統中的任何地方發生失敗,每一個算子的全部狀態都回滾到最新的全局一致 checkpoint 點。在回滾期間,將暫停全部處理。源也會重置爲與最近 checkpoint 相對應的正確偏移量。整個流應用程序基本上是回到最近一次的一致狀態,而後程序能夠從該狀態從新啓動。下圖描述了這種 checkpoint 機制的基礎知識。


Distributed snapshot

在上圖中,流應用程序在 T1 時間處正常工做,而且作了checkpoint。然而,在時間 T2,算子未能處理輸入的數據。此時,S=4 的狀態值已保存到持久存儲器中,而狀態值 S=12 保存在算子的內存中。爲了修復這種差別,在時間 T3,處理程序將狀態回滾到 S=4 並「重放」流中的每一個連續狀態直到最近,並處理每一個數據。最終結果是有些數據已被處理了屢次,但這不要緊,由於不管執行了多少次回滾,結果狀態都是相同的。

另外一種實現『精確一次』的方法是:在每一個算子上實現至少一次事件傳遞和對重複數據去重來。使用此方法的流處理引擎將重放失敗事件,以便在事件進入算子中的用戶定義邏輯以前,進一步嘗試處理並移除每一個算子的重複事件。此機制要求爲每一個算子維護一個事務日誌,以跟蹤它已處理的事件。利用這種機制的引擎有 Google 的 MillWheel[2] 和 Apache Kafka Streams。下圖說明了這種機制的要點。

At-least-once delivery plus deduplication

2、『精確一次』是真正的『精確一次』嗎?

如今讓咱們從新審視『精確一次』處理語義真正對最終用戶的保證。『精確一次』這個術語在描述正好處理一次時會讓人產生誤導。

有些人可能認爲『精確一次』描述了事件處理的保證,其中流中的每一個事件只被處理一次。實際上,沒有引擎可以保證正好只處理一次。在面對任意故障時,不可能保證每一個算子中的用戶定義邏輯在每一個事件中只執行一次,由於用戶代碼被部分執行的可能性是永遠存在的。

考慮具備流處理運算符的場景,該運算符執行打印傳入事件的 ID 的映射操做,而後返回事件不變。下面的僞代碼說明了這個操做:

Map (Event event) {
    Print "Event ID: " + event.getId()
    Return event
}

每一個事件都有一個 GUID (全局唯一ID)。若是用戶邏輯的精確執行一次獲得保證,那麼事件 ID 將只輸出一次。可是,這是沒法保證的,由於在用戶定義的邏輯的執行過程當中,隨時均可能發生故障。引擎沒法自行肯定執行用戶定義的處理邏輯的時間點。所以,不能保證任意用戶定義的邏輯只執行一次。這也意味着,在用戶定義的邏輯中實現的外部操做(如寫數據庫)也不能保證只執行一次。此類操做仍然須要以冪等的方式執行。

那麼,當引擎聲明『精確一次』處理語義時,它們能保證什麼呢?若是不能保證用戶邏輯只執行一次,那麼什麼邏輯只執行一次?當引擎聲明『精確一次』處理語義時,它們其實是在說,它們能夠保證引擎管理的狀態更新只提交一次到持久的後端存儲。

上面描述的兩種機制都使用持久的後端存儲做爲真實性的來源,能夠保存每一個算子的狀態並自動向其提交更新。對於機制 1 (分佈式快照 / 狀態檢查點),此持久後端狀態用於保存流應用程序的全局一致狀態檢查點(每一個算子的檢查點狀態)。對於機制 2 (至少一次事件傳遞加上重複數據刪除),持久後端狀態用於存儲每一個算子的狀態以及每一個算子的事務日誌,該日誌跟蹤它已經徹底處理的全部事件。

提交狀態或對做爲真實來源的持久後端應用更新能夠被描述爲剛好發生一次。然而,如上所述,計算狀態的更新 / 更改,即處理在事件上執行任意用戶定義邏輯的事件,若是發生故障,則可能不止一次地發生。換句話說,事件的處理能夠發生屢次,可是該處理的效果只在持久後端狀態存儲中反映一次。所以,咱們認爲有效地描述這些處理語義最好的術語是『有效一次』(effectively once)。

那麼,當引擎聲明『精確一次』處理語義時,它們能保證什麼呢?若是不能保證用戶邏輯只執行一次,那麼什麼邏輯只執行一次?當引擎聲明『精確一次』處理語義時,它們其實是在說,它們能夠保證引擎管理的狀態更新只提交一次到持久的後端存儲。

3、分佈式快照與至少一次事件傳遞和重複數據刪除的比較

從語義的角度來看,分佈式快照和至少一次事件傳遞以及重複數據刪除機制都提供了相同的保證。然而,因爲兩種機制之間的實現差別,存在顯着的性能差別。

機制 1(分佈式快照 / 狀態檢查點)的性能開銷是最小的**,由於引擎其實是往流應用程序中的全部算子一塊兒發送常規事件和特殊事件,而狀態檢查點能夠在後臺異步執行。可是,對於大型流應用程序,故障可能會更頻繁地發生,致使引擎須要暫停應用程序並回滾全部算子的狀態,這反過來又會影響性能。流式應用程序越大,故障發生的可能性就越大,所以也越頻繁,反過來,流式應用程序的性能受到的影響也就越大。然而,這種機制是非侵入性的,運行時須要的額外資源影響很小。

機制 2(至少一次事件傳遞加劇複數據刪除)可能須要更多資源,尤爲是存儲**。使用此機制,引擎須要可以跟蹤每一個算子實例已徹底處理的每一個元組,以執行重複數據刪除,以及爲每一個事件執行重複數據刪除自己。這意味着須要跟蹤大量的數據,尤爲是在流應用程序很大或者有許多應用程序在運行的狀況下。執行重複數據刪除的每一個算子上的每一個事件都會產生性能開銷。可是,使用這種機制,流應用程序的性能不太可能受到應用程序大小的影響。對於機制 1,若是任何算子發生故障,則須要發生全局暫停和狀態回滾;對於機制 2,失敗的影響更加局部性。當在算子中發生故障時,可能還沒有徹底處理的事件僅從上游源重放/重傳。性能影響與流應用程序中發生故障的位置是隔離的,而且對流應用程序中其餘算子的性能幾乎沒有影響。從性能角度來看,這兩種機制的優缺點以下。

分佈式快照 / 狀態檢查點的優缺點:

  • 優勢:
  • 較小的性能和資源開銷
  • 缺點:
  • 對性能的影響較大
  • 拓撲越大,對性能的潛在影響越大

至少一次事件傳遞以及重複數據刪除機制的優缺點:

  • 優勢:
  • 故障對性能的影響是局部的
  • 故障的影響不必定會隨着拓撲的大小而增長
  • 缺點:
  • 可能須要大量的存儲和基礎設施來支持
  • 每一個算子的每一個事件的性能開銷

雖然從理論上講,分佈式快照和至少一次事件傳遞加劇複數據刪除機制之間存在差別,但二者均可以簡化爲至少一次處理加冪等性。對於這兩種機制,當發生故障時(至少實現一次),事件將被重放/重傳,而且經過狀態回滾或事件重複數據刪除,算子在更新內部管理狀態時本質上是冪等的。

4、結論

在這篇博客文章中,我但願可以讓你相信『精確一次』這個詞是很是具備誤導性的。提供『精確一次』的處理語義實際上意味着流處理引擎管理的算子狀態的不一樣更新只反映一次。『精確一次』並不能保證事件的處理,即任意用戶定義邏輯的執行,只會發生一次。咱們更喜歡用『有效一次』(effectively once)這個術語來表示這種保證,由於處理不必定保證只發生一次,可是對引擎管理的狀態的影響只反映一次。兩種流行的機制,分佈式快照和重複數據刪除,被用來實現精確/有效的一次性處理語義。這兩種機制爲消息處理和狀態更新提供了相同的語義保證,可是在性能上存在差別。這篇文章並非要讓你相信任何一種機制都優於另外一種,由於它們各有利弊。

5、參考

  1. Chandy, K. Mani and Leslie Lamport.Distributed snapshots: Determining global states of distributed systems. ACMTransactions on Computer Systems (TOCS) 3.1 (1985): 63-75.
  2. Akidau, Tyler, et al. MillWheel:Fault-tolerant stream processing at internet scale. Proceedings of the VLDBEndowment 6.11 (2013): 1033-1044.
相關文章
相關標籤/搜索