一、狀態一致性
1)有狀態的流處理,內部每個算子任務都可以有自己的狀態;
2)對於流處理器內部來說,所謂的狀態一致性,其實就是我們所說的計算結果要保證準確;
3)一條數據不應該丟失,也不應該重複計算;
4)在遇到故障時可以恢復狀態,恢復以後進行重新計算,結果應該也是完全正確的;
1、狀態一致性分類
(1)AT-MOST-ONCE(最多一次)
》》當任務故障時,最簡單的做法就是什麼也不幹,既不恢復丟失的狀態,也不重播丟失的數據,At-most-once語義的含義是最多處理一次事件。
(2)AT-LEAST-ONCE(至少一次)
》》在大多數的真實應用場景中,我們希望不丟失事件,這種類型的保障稱爲at-least-once,意思是所有的事件都得到了處理,而一些事件還可能被處理多次;
(3)EXACTLY-ONCE(精確一次)
》》恰好處理一次是最嚴格的保證,也是最難實現的。恰好處理一次語義不僅僅意味着沒有事件丟失,還意味着針對每一個數據,內部狀態僅僅更新一次。
二、一致性檢查點(checkpoint)
1)Flink使用了一種輕量級快照機制——檢查點(checkpoint)來保證exactly-once語義;
2)有狀態流應用的一致檢查點,其實就是:所有任務的狀態,在某個時間點的一份拷貝(一份快照)。而這個時間點,應該是所有任務都恰好處理完一個相同的輸入數據的時候;
3)應用狀態的一致檢查點,是Flink故障恢復機制的核心;
三、端到端(end-to-end)狀態一致性
1)目前我們看到的一致性保證都是由流處理器實現的,也就是說都是在Flink流處理器內部保證的;而在真實應用中,流處理應用除了流處理器以外還包含了數據源(例如:Kafka)和輸出到持久化系統;
2)端到端的一致性保證,意味着結果的正確性貫穿了整個流處理應用的始終;每一個組件都保證了它自己的一致性;
3)整個端到端的一致性級別取決於所有組件中一致性最弱的組件;
四、端到端的精確一次(exactly-once)保證
1)內部保證——checkpoint;
2)source端——可重設數據的讀取位置
3)sink端——從故障恢復時,數據不會重複寫入外部系統
》》冪等寫入
》》事務寫入
1、冪等寫入(Idempotent Writes)
1)所謂冪等操作,是說一個操作,可以重複執行很多次,但只導致一次結果更改,也就是說,後面再重複執行就不起作用了;
2、事務寫入(Transactional Writes)
1)事務(Transaction)
》》應用程序中一系列嚴密的操作,所有操作必須成功完成,否則在每個操作中所做的所有更改都會被撤銷;
》》具有原子性:一個事務中的一系列操作要麼全部成功,要麼一個都不做;
2)實現思想:構建的事務對應着checkpoint,等到checkpoint真正完成的時候,才把所有對應的結果寫入sink系統中;
3)實現方式:預寫日誌、兩階段提交;
2.1 預寫日誌(Write-Ahead-Log,WAL)
1)把結果數據先當成狀態保存,然後在收到checkpoint完成的通知時,一次性寫入sink系統;
2)簡單易於實現,由於數據提前在狀態後端中做了緩存,所以無論什麼sink系統,都能用這種方式一批搞定;
3)DataStream API提供了一個模版類:GenericWriteAheadSink,來實現這種事務性sink;
2.2 兩階段提交(Two-Phase-Commit,2PC)
1)對於每個checkpoint,sink任務會啓動一個事務,並將接下來所有接收的數據添加到事務裏;
2)然後將這些數據寫入外部sink系統,但不提交它們——這時只是“預提交”;
3)當它收到checkpoint完成的通知時,它才正式提交事務,實現結果的真正寫入;
4)這種方式真正實現了exactly-once,它需要一個提供事務支持的外部sink系統。Flink提供了TwoPhaseCommitSinkFunction接口;
2.3 2PC對外部sink系統的要求
1)外部sink系統必須提供事務支持,或者sink任務必須能夠模擬外部系統上的事務;
2)在checkpoint的間隔期間裏,必須能夠開啓一個事務並接收數據寫入;
3)在收到checkpoint完成的通知之前,事務必須是“等待提交”的狀態。在故障恢復的情況下,這可能需要一些時間。如果這個時候sink系統關閉事務(例如:超時了),那麼未提交的數據就會丟失;
4)sink任務必須能夠在進程失敗後恢復事務;
5)提交事務必須是冪等操作;
2.4 不同Source和Sink的一致性保證
source sink |
不可重置 |
可重置 |
任意(Any) |
At-most-once |
At-least-once |
冪等 |
At-most-once |
Exactly-once (故障恢復時會出現暫時不一致) |
預寫日誌(WAL) |
At-most-once |
At-least-once |
兩階段提交(2PC) |
At-most-once |
Exactly-once |
五、Flink+Kafka端到端狀態一致性的保證
1)內部——利用checkpoint機制,把狀態存盤,發生故障的時候可以恢復,保證內部的狀態一致性;
2)source——kafka consumer作爲source,可以將偏移量保存下來,如果後續任務出現了故障,恢復的時候可以由連接器重置偏移量,重新消費數據,保證一致性;
3)sink——kafka producer作爲sink,採用兩階段提交sink,需要實現一個TwoPhaseCommitSinkFunction;
1、Exactly-once兩階段提交
1)JobManager協調各個TaskManager進行checkpoint存儲;
2)checkpoint保存在StateBackend中,默認StateBackend是內存級的,也可以改爲文件級的進行持久化保存;
3)當checkpoint啓動時,JobManager會將檢查點分界線(barrier)注入數據流;
4)barrier會在算子間傳遞下去;
5)每個算子會對當前的狀態做個快照,保存到狀態後端;
6)checkpoint機制可以保證內部的狀態一致性;
7)每個內部的transform任務遇到barrier時,都會把狀態保存到checkpoint裏面;
8)sink任務首先把數據寫入外部kafka,這些數據都屬於預提交的事務;遇到barrier時,把狀態保存到狀態後端,並開啓新的預提交事務;
9)當所有算子任務的快照完成,也就是這次的checkpoint完成時,JobManager會向所有任務發通知,確認這次checkpoint完成;
10)sink任務收到確認通知,正式提交之前的事務,kafka中未確認數據改爲“已確認”;
2、 Exactly-once兩階段提交步驟
1)第一條數據來了之後,開啓一個kafka的事務(Transaction),正常寫入kafka分區日誌但標記爲未提交,這就是“預提交”;
2)JobManager觸發checkpoint操作,barrier從source開始向下傳遞,遇到barrier的算子將狀態存入狀態後端,並通知JobManager;
3)sink連接器收到barrier,保存當前狀態,存入checkpoint,通知JobManager,並開啓下一階段的事務,用於提交下一個檢查點的數據;
4)JobManager收到所有任務的通知,發出確認信息,表示checkpoint完成;
5)sink任務收到JobManager的確認信息,正式提交這段時間的數據;
6)外部Kafka關閉事務,提交的數據可以正常消費了;