【Flink入門修煉】2-3 Flink Checkpoint 原理機制

大数据王小皮發表於2024-04-25

如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢?
單機和多機會遇到什麼不同的問題?
Flink Checkpoint 是做什麼用的?原理是什麼?

一、什麼是 Checkpoint?

Checkpoint 是對當前執行狀態的完整記錄。程式重啟後能從 Checkpoint 中恢復出輸入資料讀取到哪了,各個運算元原來的狀態是什麼,並繼續執行程式。
即用於 Flink 的故障恢復。
這種機制保證了實時程式執行時,即使突然遇到異常也能夠進行自我恢復。

二、如何實現 Checkpoint 功能?

如果讓你來設計,對於流式應用如何做到故障恢復?
我們從最簡單的單機單執行緒看起。

一)單機情況

同步執行,每次只處理一條資料

image.png

很簡單,這種情況下,整個流程一次只處理一條資料。

  • 資料到 Write 階段結束,各個運算元記錄一次各自狀態資訊(如讀取的 offset、中間運算元的狀態)
  • 遇到故障需要恢復的時候,從上一次儲存的狀態開始執行
  • 當然為了降低記錄帶來的開銷,可以攢一批之後再記錄。

同時處理多條資料

每個計算節點還是隻處理一條資料,但該節點空閒就可以處理下一條資料。
image.png

如果還按照一個資料 Write 階段結束開始儲存狀態,就會出現問題:

  • 前面節點的狀態,在處理下一個資料時被改過了
  • 從此時儲存的記錄恢復,前面的節點會出現重複處理的問題
  • 此時被稱為 - 確保資料不丟(At Least Once)

一種解決方式:

  • 在輸入資料中,定期插入一個 barrier
  • 各運算元遇到 barrier 就開始做狀態保留,並且不再接收新資料的計算。
  • 當前運算元狀態保留後,將 barrier 傳遞給下一個運算元,並重覆上面的步驟。
  • 當 barrier 傳遞到最後一個運算元,並完成狀態保留後,本次狀態保留完成。

這樣,各個節點儲存的都是相同資料節點時的狀態。
故障恢復時,能做到不重複處理資料,也就是精確一次(Exactly-once)。
image.png

但這裡,你可能會發現一個問題:

  • 資料已經寫出了怎麼辦?在兩個儲存點之間,已經把結果寫到外部了,重啟後不是又把部分資料再寫了一次?

這裡實際是「程式內部精確一次」「端到端精確一次」
那麼如何做到「端到端精確一次」?

  • 方案一:最後一個 sink 運算元不直接向外部寫出,等到 barrier 來了,才把這一批資料批次寫出去
  • 方案二:兩階段提交。需要 sink 端支援(如 kafka)。
    • 方式類似於 MySQL 的事務。
    • sink 端正常向外部寫出,不過輸出端處於 pre-commit 狀態,這些資料還不可讀取
    • 當 sink 端等到 barrier 時,將輸出端資料變為 committed,下游輸出端的資料才正式可讀

不過以上方法為了做到端到端精確一次,會帶來資料延遲問題。(因為要等 Checkpoint 做完,資料才實際可讀)。

解決資料延遲有一種方案:

  • 方案:冪等寫入。同樣一條資料,無論寫入多少次對輸出端看來都是一樣的。(比如按照主鍵重複寫這一條資料,並且資料本身沒變化)

二)重要概念介紹

一致性級別

前面的例子中,我們提到了部分一致性級別,這裡我們總結下。在流處理中,一致性可以分為 3 個級別:

  • at-most-once(最多一次): 這其實是沒有正確性保障的委婉說法——故障發生之後,計數結果可能丟失。
  • at-least-once (至少一次): 這表示計數結果可能大於正確值,但絕不會小於正確值。也就是說,計數程式在發生故障後可能多算,但是絕不會少算。
  • exactly-once (精確一次): 這指的是系統保證在發生故障後得到的計數結果與正確值一致。恰好處理一次是最嚴格的保證,也是最難實現的。

按區間分:

  • 程式(Flink)內部精確一次
  • 端到端精確一次

Checkpoint 中保留的是什麼資訊?

🤔 如果是你來設計,checkpoint 都需要保留哪些資訊,才能讓程式恢復執行?
【這裡說的就是 state
考慮一個開發需求:單詞計數。
從 kafka 中讀資料,處理邏輯是將輸入資料拆分成單詞,有一個 map 記錄各個單詞的數量,最後輸出。

  • 從輸入流中,拆分單詞
  • 將統計的結果放到記憶體中一個 Map 集合,單詞做為 key,對應的數量做為 value

想要恢復的時候還能接著上次的狀態來,要麼就需要幾個資訊:

  • 處理到哪條資料了
  • 中間狀態是啥
  • 資料寫出到哪條了

以及,上述資訊應是針對同一條資料的。否則狀態就亂了。
那麼可以得到,保留的資訊是:

source 中間運算元 sink
已輸入的資料(offset) [<hello, 5>, <world, 10>, ...] 寫出到第幾條了

三)多機多程序

隨著業務的發展,單機已經不能滿足需求了,開始並行分散式的處理。
讀取、處理、寫出,也不再是一個程序從頭到尾幹完,會拆分到多個機器上執行。也不再等待一條資料處理完,才處理下一條。
image.png

多機多執行緒,問題就開始變得複雜起來:

  • 如何確保狀態擁有精確一次的容錯保證?
  • 如何在分散式場景下,替多個擁有本地狀態的運算元產生一個全域一致的快照?
  • 對於流合併,合併節點會受到多個 barrier 如何處理?
  • 如何在不中斷運算的前提下產生快照?

🤔 先思考下,如果還用單執行緒中 barrier 的方式來處理。會遇到什麼問題,該如何解決?

處理流程

我們還是在資料流中插入 barrier。

  • 到達第一個 source 節點和之前的沒區別,source 節點開始儲存狀態(offset)

image.png

  • 接下來,source 將 barrier 拆分為兩個,分別發往下游的運算元

image.png

  • 下游運算元收到 barrier,開始記錄狀態

image.png

  • 關鍵是最後的 operator#2,它會收到多個 barrier
    • barrier 的初始目的是,收到 barrier 表示前面的資料都處理完了,要開始儲存狀態了
    • 兩個綠色的節點(operator#1)分別傳送 barrier,代表兩個 barrier 之前處理過的資料,實際都是第一個藍色節點(source)barrier 之前的資料。
    • 那麼最後的橙色節點(operator#2),理應收到所有由綠色節點(operator#1)傳送的 barrier,才代表資料已經收全了,可以開始儲存狀態。【叫做 barrier 對齊】

image.png

對於多分支合併的情況,在等待所有 barrier 到齊的過程中:

  • 先收到 barrier 的分支,還會有資料不斷流入
  • 為了能做到精確一次(Exactly-once),就不能處理這些資料,需要先快取起來,否則這個節點的狀態就不對了
  • 上面一條反過來說,如果不等,直接處理,那麼就是至少一次(At Least Once)的效果。(想想在故障恢復的時候,是不是就會重複計算了)

如何在不中斷運算的前提下產生快照?
前面做快照,我們假設的是節點收到 barrier 後,就不再接收新資料,把當前節點狀態儲存後,再接收新資料,然後把 barrier 再向後傳遞。
那,是否必須這樣序列來呢?

  • 卡住新資料,儲存當前狀態,這裡必須序列,不序列狀態就亂了
  • 但是,向後傳送 barrier 可以同時做,不影響當前節點的儲存

那,後面節點儲存完了,前面節點還沒儲存完怎麼辦?

  • 沒關係,一次 checkpoint 成功,需要等待所有節點都成功才行,儲存的先後順序無所謂

程式中如何開啟 checkpoint

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// 開啟 checkpoint,並設定間隔 ms
env.enableCheckpointing(1000);
// 模式 Exactly-Once、At-Least-Once
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
// 兩個 checkpoint 之間最小間隔
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
// 超時時間
env.getCheckpointConfig().setCheckpointTimeout(60000);
// 同時執行的 checkpoint 數量(比如上一個還沒執行完,下一個已經觸發開始了)
env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
// 當使用者取消了作業後,是否保留遠端儲存上的Checkpoint資料
env.getCheckpointConfig().enableExternalizedCheckpoints(ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);

checkpoint 儲存

Flink 開箱即用地提供了兩種 Checkpoint 儲存型別:

  • JobManagerCheckpointStorage
    • 將 Checkpoint 快照儲存在 JobManager 的堆記憶體中
  • FileSystemCheckpointStorage
    • 放到 HDFS 或本地磁碟中

四、小結

本節介紹了 Flink Checkpoint 故障恢復機制。從單機單執行緒,到多機多執行緒一步步分析如何實現狀態儲存和故障恢復。
同時對一致性級別進行了探討,對程式內部和端到端一致性的實現方式給出了可行的方案。
後續會對 Checkpoint 程式內部實現原理進行剖析。


參考文章:
Flink Checkpoint 深入理解-CSDN部落格
漫談 Flink - Why Checkpoint - Ying
Flink之Checkpoint機制-阿里雲開發者社群 (圖不錯)
Flink 狀態一致性、端到端的精確一次(ecactly-once)保證 - 掘金
硬核!八張圖搞懂 Flink 端到端精準一次處理語義 Exactly-once(深入原理,建議收藏)-騰訊雲開發者社群-騰訊雲

相關文章