1.前言
哈嘍函似,大家好磁奖。在第二章節(jié)中我聊了聊狀態(tài)后端和檢查點(diǎn)相關(guān)的內(nèi)容糜值,如果你仔細(xì)看完就能夠很清楚的知道满钟,F(xiàn)link是如何保存自己引以為傲的狀態(tài)的了。并且我也在上一章節(jié)中說了瘫筐,F(xiàn)link能夠依賴檢查點(diǎn)機(jī)制來實(shí)現(xiàn)自身的精確一致性矩桂,所以這篇文章咱們就簡單聊聊它所謂的精準(zhǔn)一次性是如何實(shí)現(xiàn)的软族。(檢查點(diǎn)是根據(jù)在數(shù)據(jù)中插入“分界線”標(biāo)志來觸發(fā)的)
2.精準(zhǔn)一次性
從本文的標(biāo)題能夠看出添谊,今天要聊的是端到端的狀態(tài)一致性财喳,那么首先要先知道這個(gè)端到端是什么意思。我們試想一下,F(xiàn)link處理數(shù)據(jù)耳高,肯定是要有數(shù)據(jù)來源和數(shù)據(jù)發(fā)送點(diǎn)的扎瓶,也就是source和sink。那么泌枪,如果要聊端到端的精準(zhǔn)一次性概荷,就要對這個(gè)兩個(gè)“端”字進(jìn)行拆解,分為輸入端與Flink之間的精準(zhǔn)一次性碌燕,和Flink與輸出端之間的精準(zhǔn)一次性误证。
2.1.1 輸入端的精準(zhǔn)一次性
輸入端的精準(zhǔn)一次性比較好理解,你只需要明確一點(diǎn)陆蟆,只要向Flink發(fā)送數(shù)據(jù)的source端具備數(shù)據(jù)重放的功能就好雷厂。還記得上一小節(jié)提到的檢查點(diǎn)機(jī)制嗎惋增?它是對程序運(yùn)行時(shí)的某個(gè)時(shí)間點(diǎn)所有狀態(tài)的一次快照叠殷,在做快照的時(shí)候,source算子會讀取數(shù)據(jù)源的偏移量信息诈皿,一起保存在檢查點(diǎn)中林束,這就能夠在故障恢復(fù)的時(shí)候讓source算子根據(jù)自己保存的這個(gè)偏移量信息,去數(shù)據(jù)源中重新讀取數(shù)據(jù)稽亏。所以數(shù)據(jù)源頭這方面壶冒,精準(zhǔn)一次性比較好搞,因?yàn)镕link內(nèi)部干了很多事情截歉。當(dāng)然了胖腾,這個(gè)source端一定要具備數(shù)據(jù)保存和數(shù)據(jù)回放的能力,如果不存數(shù)的話瘪松,就算真記錄了偏移量也是白搭咸作。
2.1.2 輸出端的精準(zhǔn)一次性
輸入端說完了,接下來就說說輸出端宵睦。這一塊其實(shí)就不太好弄了记罚,因?yàn)镕link將數(shù)據(jù)發(fā)送到sink方。它的角色就仿若souce到flink一樣壳嚎。(ps:souce->flink === flink -->sink桐智,都是一個(gè)東西向另外一個(gè)東西發(fā)送數(shù)據(jù))。但是sink端的外部存儲系統(tǒng)沒辦法像Flink一樣烟馅,通過保存狀態(tài)的方式來完成精準(zhǔn)一次性说庭。但是在開發(fā)過程中,精準(zhǔn)一次性很多場景都非常重要郑趁,所以為了能夠?qū)崿F(xiàn)Flink到sink端的精準(zhǔn)一次性刊驴,還是需要對sink端有一些要求的。
主要的要求內(nèi)容一共有兩個(gè)穿撮,分別是“事務(wù)”和“冪等性”:
我們首先來聊一聊冪等缺脉,冪等這個(gè)東西很神奇痪欲。它就像一個(gè)不允許重復(fù)的集合一樣,在冪等的環(huán)境下攻礼,一個(gè)操作可以重復(fù)多次业踢,但是次操作所導(dǎo)致的結(jié)果就只有一次。有了冪等性礁扮,數(shù)據(jù)無論寫多少次知举,都不會影響到sink端的結(jié)果,但是如果想要進(jìn)行冪等性寫出太伊,那就要求sink端需要支持冪等雇锡,還是有一些場景上的限制的。
冪等性聊完了就聊聊事務(wù)僚焦,事務(wù)相對于冪等性的要求它的限制要少一些锰提。其實(shí)在聊精準(zhǔn)一次性之前,大家要明確一個(gè)點(diǎn)芳悲,那就是sink端面臨的問題不是數(shù)據(jù)會不會丟失立肘,而是數(shù)據(jù)會不會被重復(fù)計(jì)算。這是因?yàn)榧词故菙?shù)據(jù)因?yàn)楣收蟻G失名扛,source端也會通過檢查點(diǎn)來對數(shù)據(jù)進(jìn)行重新讀寫谅年,所以數(shù)據(jù)丟失的幾率被降得很低。所以重復(fù)計(jì)算才是sink端的難題肮韧。
我們都知道融蹂,事務(wù)的特性是:如果我本次的活干到一半突然出現(xiàn)故障,那么這次干的內(nèi)容我會全部收回弄企。當(dāng)然這是粗俗的說法超燃,如果嚴(yán)格一點(diǎn),那就是:事務(wù)具有四個(gè)基本特性:ACID桩蓉,即原子淋纲、一致、隔離院究、持久這四大特性洽瞬。通過使用這四大特性,就能夠保證如果本次的操作沒有完成业汰,那這次操作過程中完成的部分也會被撤銷伙窃。
那就用這個(gè)理念,來套入Flink與sink之間的處理样漆。如果我們寫一個(gè)事務(wù)为障,用這個(gè)事務(wù)和檢查點(diǎn)綁定在一起,通過這個(gè)事務(wù)向外部寫出數(shù)據(jù),當(dāng)Sink算子觸發(fā)檢查點(diǎn)保存的時(shí)候鳍怨,開啟保存狀態(tài)的同時(shí)就開啟一個(gè)事務(wù)呻右,接下來的數(shù)據(jù)都寫在這個(gè)事務(wù)里面。只有檢查點(diǎn)完成了保存鞋喇,那么事務(wù)就可以提交声滥,數(shù)據(jù)就算是成功寫出,可以使用了侦香。如果程序出現(xiàn)故障了落塑,狀態(tài)保存失敗,就會根據(jù)檢查點(diǎn)中的內(nèi)容對數(shù)據(jù)重新讀寫罐韩,事務(wù)也肯定是失敗了憾赁,就會回退。之前的操作也會作廢散吵,也就證明了數(shù)據(jù)沒有多余寫出了龙考。
3.事務(wù)的兩種實(shí)現(xiàn)方式(預(yù)寫日志、兩階段提交)
為了能夠滿足Flink與sink端之間的狀態(tài)精準(zhǔn)一次性错蝴,也明確了在兩種實(shí)現(xiàn)理論中事務(wù)比較牛洲愤,那就再來聊一聊事務(wù)實(shí)現(xiàn)的兩種方式:預(yù)寫日志颓芭、兩階段提交顷锰。當(dāng)然了,這個(gè)TMD數(shù)據(jù)寫出端也需要支持事務(wù)才行亡问。就像我之前使用到的Clickhouse官紫,它既不支持事務(wù)也不支持冪等,只能通過本身的副本合并樹來實(shí)現(xiàn)虛假的精準(zhǔn)一次性(我現(xiàn)階段就這個(gè)水平州藕,可能有的人會手寫更牛叉的自定義Sink也說不定)束世。
3.1 預(yù)寫日志
預(yù)寫日志首先需要把數(shù)據(jù)作為日志狀態(tài)保存起來,既然它是日志狀態(tài)了床玻,那就證明程序在觸發(fā)檢查點(diǎn)的時(shí)候就能夠?qū)⑺黄鹨钥煺盏姆绞酱鎯Φ酵獠肯到y(tǒng)中做持久化存儲毁涉。當(dāng)檢查點(diǎn)成功保存之后,再把結(jié)果一次性寫出就好锈死。這么搞是不是就算是完成精準(zhǔn)一次性了呢贫堰?答案是,是的待牵!但是預(yù)寫日志有個(gè)問題其屏,那就是它是把一個(gè)小階段之間的數(shù)據(jù)全量寫出,雖然在宏觀角度去看這一個(gè)階段很小缨该,但是Flink處理讀取數(shù)據(jù)的能力賊強(qiáng)偎行,就這一小階段就會產(chǎn)生大量數(shù)據(jù),一次性寫出就會對集群造成一定的壓力。就相當(dāng)于把流處理變成了批處理(spark震怒8蛱弧Oㄔ啤!)
這種方法看起來有點(diǎn)美好妙真,但是實(shí)際上它有問題皱碘。它把流處理變成了批處理,那必然要知道這次批操作成沒成功隐孽,所以在數(shù)據(jù)寫出完成之后癌椿,會還給sink一個(gè)成功的信號,只有明確這個(gè)信息是真的完成了菱阵,才能證明Checkpoint是成功的踢俄,同時(shí)也要把這個(gè)返回的成功信息做一個(gè)保存,用來證明這次批處理的成功晴及。這種情況會面臨的問題就是都办,當(dāng)數(shù)據(jù)成功寫出了,返回成功信號的時(shí)候出事兒了虑稼,那故障恢復(fù)后Flink確認(rèn)不了這次成沒成功琳钉,就會重新發(fā)送數(shù)據(jù),數(shù)據(jù)就又會重復(fù)發(fā)送了蛛倦。
這個(gè)功能DateSteam API提供了一個(gè)模板類來幫忙完成歌懒。
GenericWriteAheadSink
3.2兩階段提交
預(yù)寫日志說完了,就來談?wù)剝呻A段提交吧溯壶。它比預(yù)寫日志還要牛一點(diǎn)及皂,但是限制也是一大堆,比較難搞且改。在正式說兩階段提交之前验烧,要明確兩個(gè)概念。第一個(gè)是兩階段是由預(yù)提交和正式提交兩部分組成又跛,預(yù)提交階段的數(shù)據(jù)內(nèi)容是不能夠使用的碍拆。
當(dāng)?shù)谝粭l數(shù)據(jù)來時(shí),或者是sink接收到檢查點(diǎn)的“分界線”時(shí)慨蓝,就會開啟一個(gè)事務(wù)感混,這個(gè)事務(wù)會代替sink來進(jìn)行數(shù)據(jù)的寫出操作,但是在這個(gè)階段菌仁,所有寫出的操作都是不可用的浩习,也就是預(yù)提交階段。而sink這個(gè)時(shí)候會進(jìn)入一個(gè)等待狀態(tài)济丘,它需要接收J(rèn)obManager發(fā)送給它的一個(gè)檢查點(diǎn)成功保存的信號谱秽,一旦它接收到這個(gè)信息洽蛀,那么sink端的這個(gè)事務(wù)就會提交,預(yù)提交階段所有不可用的數(shù)據(jù)便會變成可使用的了疟赊。
Flink同樣提供了一個(gè)模板sink:
TwoPhaseCommitSinkFunction
4.結(jié)語
在這篇文章里面郊供,我聊了聊我理解的有關(guān)于狀態(tài)一致性的內(nèi)容,總的來說得到了一個(gè)結(jié)論近哟,那就是精準(zhǔn)一次性代價(jià)頗高驮审,逼事賊多。而我目前用到的clickhouse既不支持事務(wù)吉执、也不支持冪等疯淫。所以我還沒有解除過重寫上述兩個(gè)接口的過程。但是戳玫,在進(jìn)行實(shí)時(shí)數(shù)據(jù)處理的過程中熙掺,大多都是在Kafka與Flink之間進(jìn)行數(shù)據(jù)的來回傳遞,分層操作也是如此咕宿。所以币绩,我們在下一篇文章中就聊聊,有關(guān)于 Kafka -> Flink -> Kafka 之間的狀態(tài)一致性吧府阀。