What is State
雖然數據流中的許多操作一次只查看一個單獨的事件(例如事件解析器),但某些操作會記住多個事件的信息(例如窗口算子)。 這些操作稱爲有狀態的(stateful)。
有狀態操作的一些示例:
- 當應用程序搜索某些事件模式(event patterns)時,狀態(state)將存儲迄今爲止遇到的事件序列。
- 當每分鐘/小時/天 聚合事件時,狀態(state)保存待處理的聚合。
- 當通過流中的數據點訓練機器學習模型時,狀態(state)保存模型參數的當前版本。
- 當需要管理歷史數據時,狀態(state)允許有效訪問過去發生的事件。
Flink 需要了解狀態(state),以便使用檢查點(checkpoint)和保存點(savepoint)實現容錯(fault-tolerant)。
額外的,狀態(state)還允許重新擴展 Flink 應用程序,這意味着 Flink 負責跨並行實例重新分配狀態(state)。
在處理狀態(state)時,瞭解 Flink 的狀態後端(state-backends)也可能很有用。 Flink 提供了不同的狀態後端(state-backends)來指定狀態的存儲方式和位置。
Keyed State
鍵控狀態(Keyed State)被維護在可以被認爲是嵌入式鍵/值存儲中。 狀態(state)與有狀態算子(the stateful operators)讀取的流一起嚴格分區和分佈。 因此,對鍵/值狀態的訪問只能在鍵控流(Keyed Stream)上進行,即在鍵控/分區數據交換之後,並且僅限於與當前事件的鍵關聯的值。對齊 流和狀態的鍵(keys of stream and state)可確保所有狀態更新都是本地操作,從而保證一致性而無需事務開銷。 這種對齊還允許 Flink 重新分配狀態並透明地調整流分區。
鍵控狀態(Keyed State)進一步組織成所謂的鍵組(Key Groups)。 Key Groups 是 Flink 可以重新分配 Keyed State 的原子單元; 鍵組(Key Groups)的數量與定義的最大並行度完全相同。在執行期間,鍵控(Keyed)算子的每個並行實例都使用一個或多個鍵組的鍵。
State Persistence
Flink 使用流重放(stream replay)和檢查點(checkpoint)的組合來實現容錯。檢查點(checkpoint)標記每個輸入流中的特定點以及每個算子的相應狀態。通過恢復算子的狀態並從檢查點(checkpoint) 重放(replay)記錄,可以從檢查點(checkpoint)恢復數據流,同時保持一致性(僅一次處理語義)。
檢查點(checkpoint)間隔是一種權衡執行期間容錯開銷與恢復時間(需要重放(replay)的記錄數)的方法。
容錯機制不斷地繪製分佈式流數據流的快照(snapshots)。 對於小狀態的流式應用程序來說,這些快照非常輕量,可以頻繁繪製,而不會對性能產生太大影響。 流應用程序的狀態存儲在可配置的位置,通常存儲在分佈式文件系統中。
如果發生程序故障(由於機器、網絡或軟件故障),Flink 會停止, 然後根據最近的檢查點(checkpoint)恢復Operators(算子)重啓應用。輸入流重置爲狀態快照點,作爲重新啓動的並行數據流的一部分進行處理的任何記錄都保證不會影響之前的檢查點(checkpoint)狀態。
Checkpointing
Flink 容錯機制的核心部分是繪製分佈式數據流和算子狀態的一致快照。 這些快照充當一致的檢查點(checkpoint),系統在發生故障時可以回退到這些檢查點(checkpoint)。 Flink 繪製這些快照的機制在"分佈式數據流的輕量級異步快照"中進行了描述。它受到分佈式快照標準 Chandy-Lamport 算法的啓發,專門針對 Flink 的執行模型量身定製。
請記住,與檢查點(checkpoint)有關的所有事情都可以異步完成。 檢查點(checkpoint)屏障(checkpoint barrier)不會以鎖定(lock)步驟運行,並且可以異步快照Operators(算子)狀態。
Barriers
Flink 分佈式快照的核心元素是stream barrier。 這些barrier被注入到數據流中,並作爲數據流的一部分與record一起流動。 barrier永遠不會超越record,它們嚴格按照順序流動。barrier將數據流中的record分爲進入當前快照的records和進入下一個快照的記錄集records。 每個barrier都帶有其record推送到其前面的快照的ID。 record不會中斷數據的傳輸,因此非常輕。 來自不同快照的多個barrier可以同時存在於流中,這意味着各種快照可能同時發生。
stream barrier被注入到流源處的並行數據流中。 快照 n 的barrier被注入的點(我們稱之爲 Sn)是源流中快照覆蓋數據的位置。 例如,在 Apache Kafka 中,該位置將是分區中最後一條記錄的偏移量。 這個位置 Sn 被報告給檢查點(checkpoint)協調器(Flink 的 JobManager)。
然後barrier流向下游。 當中間Operators(算子)從其所有輸入流接收到快照 n 的屏障時,它將向其所有輸出流發出快照 n 的barrier。 一旦sink Operator(Stream DAG 的末端)從其所有輸入流接收到barrier n,它就會向檢查點(checkpoint)協調器確認該快照 n. 當所有接收器都確認快照後,該快照就被認爲已完成。
一旦快照 n 完成,作業將不再向源請求 Sn 之前的record,因爲此時這些record(及其後的record)將已經穿過整個數據流拓撲。
接收多個輸入流的Operators(算子)需要在快照barrier上對齊輸入流。 上圖說明了這一點:
- 一旦Operators(算子)從輸入流接收到快照barrier n,它就無法處理該流中的任何進一步record,直到它也從其他輸入接收到barrier n。 否則,它將混合屬於快照 n 的記錄和屬於快照 n+1 的記錄。
- 一旦最後一個流接收到barrier n,Operators(算子)就會發出所有掛起的傳出records,然後發出快照 n barrier本身。
- 它對狀態進行快照並恢復處理來自所有輸入流的records,在處理來自流的records之前處理來自輸入緩衝區的記錄。
- 最後,Operators(算子)將狀態異步寫入狀態後端。
請注意,具有多個輸入的所有算子以及在 shuffle 後消耗多個上游子任務的輸出流的算子都需要對齊。
Snapshotting Operator State
當算子包含任何形式的State時,該State也必須是快照的一部分。
算子在從輸入流接收到所有快照barrier時以及在將barrier發送到輸出流之前對其狀態進行快照。 此時,所有來自barrier之前的records的狀態更新都已經完成,並且沒有依賴於應用barrier之後的記錄的更新。 由於快照的狀態可能很大,因此它存儲在可配置的狀態後端中。 默認情況下,存儲在 JobManager 的內存,但對於生產使用,應配置分佈式可靠存儲(例如 HDFS)。 存儲狀態後,算子確認checkpoint,將快照barrier發送到輸出流中,然後繼續。
快照的結果包含:
- 對於每個並行流數據源,啓動快照時流中的offset/position
- 對於每個算子,其內的state也會作爲快照的一部分存儲
Recovery
這種機制下的恢復很簡單:發生故障時,Flink 選擇最新完成的checkpoint k。然後,系統重新部署整個分佈式數據流,併爲每個算子提供作爲checkpoint k的一部分快照的狀態。source端從位置 Sk 開始讀取流。 例如,在 Apache Kafka 中,這意味着告訴消費者從offset Sk 開始獲取。
如果state是增量快照的,則算子從最新完整快照的state開始,然後對該state應用一系列增量快照更新。
Unaligned Checkpointing
檢查點(checkpoint)也可以不對齊地執行。 基本思想是,只要流中的數據成爲算子 state的一部分,checkpoint就可以取代所有流中的數據。
請注意,這種方法實際上更接近 Chandy-Lamport 算法,但 Flink 仍然在source中插入barrier以避免checkpoint協調器過載。
該圖描述了算子如何處理未對齊的checkpoint barrier:
- 算子對存儲在其輸入緩衝區中的第一個barrier做出反應。
- 它通過將barrier添加到輸出緩衝區的末尾,立即將barrier轉發給下游算子。
- 算子將所有被超越的record標記爲異步存儲,並創建其自身state的快照。
因此,算子僅短暫停止輸入處理以標記緩衝區、轉發barrier並創建其他state的快照。
未對齊的checkpoint可確保barrier儘快到達sink端。 它特別適合具有至少一條緩慢移動數據路徑的應用程序,其中對齊時間可能長達數小時。 然而,由於它增加了額外的 I/O 壓力,因此當狀態後端的 I/O 成爲瓶頸時,它就無濟於事了。
Unaligned Recovery
首先恢復算子運行中的數據,然後開始處理來自未對齊checkpoint的上游算子的任何數據。 除此之外,與Recovery的步驟是一致的
State Backends
存儲鍵/值索引的具體數據結構取決於所選的狀態後端。 一個狀態後端將數據存儲在內存中的哈希映射中,另一個狀態後端使用 RocksDB 作爲鍵/值存儲。 除了定義保存狀態的數據結構之外,狀態後端還實現了獲取鍵/值狀態的時間點快照並將該快照存儲爲checkpoint的一部分的邏輯。 可以在不更改應用程序邏輯的情況下配置狀態後端。
Savepoints
所有使用checkpoint的程序都可以從savepoint恢復執行。savepoint允許更新您的程序和 Flink 集羣,而不會丟失任何狀態。
savepoint是手動觸發的checkpoint,它進行程序snapshot並將其寫入狀態後端。 爲此,他們依賴定期checkpoint機制。
savepoint與checkpoint類似,不同之處在於它是由用戶觸發的,並且在新的checkpoint完成時不會自動過期。
Exactly Once vs. At Least Once
對齊步驟可能會增加stream程序的延遲。 通常,這種額外的延遲約爲幾毫秒,但我們已經看到一些異常值的延遲明顯增加的情況。 對於所有記錄都需要一致超低延遲(幾毫秒)的應用程序,Flink 有一個開關可以在checkpoint期間跳過 流對齊。 一旦算子 看到來自每個輸入的checkpoint barrier,仍然會繪製checkpoint快照。
當跳過對齊時,算子會繼續處理所有輸入,即使在checkpoint n 的一些checkpoint barrier到達之後也是如此。 這樣,在獲取checkpoint n 的狀態快照之前,算子還會處理屬於checkpoint n+1 的元素。在恢復時,這些record將作爲重複項出現,因爲它們都包含在checkpoint n 的狀態快照中,並且將在checkpoint n 之後作爲數據的一部分重放。