分布式系統(tǒng):數(shù)據(jù)一致性解決方案
點(diǎn)擊上方藍(lán)色字體,選擇“設(shè)為星標(biāo)”

在分布式系統(tǒng)中,隨著系統(tǒng)架構(gòu)演進(jìn),原來(lái)的原子性操作會(huì)隨著系統(tǒng)拆分而無(wú)法保障原子性從而產(chǎn)生一致性問(wèn)題,但業(yè)務(wù)實(shí)際又需要保障一致性,下面我從學(xué)習(xí)和實(shí)戰(zhàn)運(yùn)用總結(jié)一下分布式一致性解決方案。
1. CAP & Base理論
?
CAP定理指的是在一個(gè)分布式系統(tǒng)中,一致性(Consistency)、可用性(Availability)、分區(qū)容錯(cuò)性(Partition tolerance)。這三個(gè)要素最多只能同時(shí)實(shí)現(xiàn)兩點(diǎn),不可能三者兼顧:
一致性:在分布式系統(tǒng)中的所有數(shù)據(jù)備份,在同一時(shí)刻是否同樣的值。
分區(qū)容錯(cuò)性:可靠性,無(wú)論應(yīng)用程序或系統(tǒng)發(fā)生錯(cuò)誤,還是用戶以意外或錯(cuò)誤的方式使用,軟件系統(tǒng)都能繼續(xù)運(yùn)行。
可用性:在集群中一部分節(jié)點(diǎn)故障后,集群整體是否還能響應(yīng)客戶端的讀寫請(qǐng)求。
?

?
CAP理論3選2是偽命題,實(shí)際上必須從A和C選擇一個(gè)和P組合,更進(jìn)一步基本上都會(huì)選擇A,相比一致性,系統(tǒng)一旦不可用或不可靠都可能會(huì)造成整個(gè)站點(diǎn)崩潰,所以一般都會(huì)選擇AP。但是不一致的問(wèn)題也不能忽略,使用最終一致是比較好的辦法。
?
BASE理論是對(duì)CAP中一致性和可用性權(quán)衡的結(jié)果,其來(lái)源于對(duì)大規(guī)模互聯(lián)網(wǎng)系統(tǒng)分布式實(shí)踐的總結(jié), 是基于CAP定理逐步演化而來(lái)的。BASE理論的核心思想是:即使無(wú)法做到強(qiáng)一致性,但每個(gè)應(yīng)用都可以根據(jù)自身業(yè)務(wù)特點(diǎn),采用適當(dāng)?shù)姆绞絹?lái)使系統(tǒng)達(dá)到最終一致性。接下來(lái)看一下BASE中的三要素:
基本可用(Basically Available):基本可用是指分布式系統(tǒng)在出現(xiàn)不可預(yù)知故障的時(shí)候,允許損失部分可用性。注意,這絕不等價(jià)于系統(tǒng)不可用。比如:
響應(yīng)時(shí)間上的損失。正常情況下,一個(gè)在線搜索引擎需要在0.5秒之內(nèi)返回給用戶相應(yīng)的查詢結(jié)果,但由于出現(xiàn)故障,查詢結(jié)果的響應(yīng)時(shí)間增加了1~2秒
系統(tǒng)功能上的損失:正常情況下,在一個(gè)電子商務(wù)網(wǎng)站上進(jìn)行購(gòu)物的時(shí)候,消費(fèi)者幾乎能夠順利完成每一筆訂單,但是在一些節(jié)日大促購(gòu)物高峰的時(shí)候,由于消費(fèi)者的購(gòu)物行為激增,為了保護(hù)購(gòu)物系統(tǒng)的穩(wěn)定性,部分消費(fèi)者可能會(huì)被引導(dǎo)到一個(gè)降級(jí)頁(yè)面。
軟狀態(tài)(Soft State):軟狀態(tài)指允許系統(tǒng)中的數(shù)據(jù)存在中間狀態(tài),并認(rèn)為該中間狀態(tài)的存在不會(huì)影響系統(tǒng)的整體可用性,即允許系統(tǒng)在不同節(jié)點(diǎn)的數(shù)據(jù)副本之間進(jìn)行數(shù)據(jù)同步的過(guò)程存在延時(shí)。
最終一致(Eventually Consistent):最終一致性強(qiáng)調(diào)的是所有的數(shù)據(jù)副本,在經(jīng)過(guò)一段時(shí)間的同步之后,最終都能夠達(dá)到一個(gè)一致的狀態(tài)。因此,最終一致性的本質(zhì)是需要系統(tǒng)保證最終數(shù)據(jù)能夠達(dá)到一致,而不需要實(shí)時(shí)保證系統(tǒng)數(shù)據(jù)的強(qiáng)一致性。
?
最終一致的核心做法就是通過(guò)記錄對(duì)應(yīng)操作并在操作失敗時(shí)不斷進(jìn)行重試直到成功為止。
2. 重試
在出現(xiàn)一致性問(wèn)題時(shí)如果系統(tǒng)的并發(fā)或不一致情況較少,可以先使用重試來(lái)解決。
?

在調(diào)用Service B超時(shí)或失敗時(shí)進(jìn)行重試:
同步調(diào)用,捕獲異常重新調(diào)用Service B。
異步消息,捕獲異常發(fā)送延遲消息重新調(diào)用Service B。
異步線程,捕獲異常開(kāi)啟異步線程重新調(diào)用Service B。
?
如果重試還是不能解決問(wèn)題,那么需要使用分布式事務(wù)來(lái)解決。
3. 分布式事務(wù)
對(duì)于分布式一致性問(wèn)題可以采用分布式事務(wù)來(lái)解決。
3.1 2PC-XA協(xié)議
XA事務(wù)由一個(gè)或多個(gè)資源管理器(Resource Managers)、一個(gè)事務(wù)管理器(Transaction Manager)以及一個(gè)應(yīng)用程序(Application Program)組成。
資源管理器(RM):參與者。提供訪問(wèn)事務(wù)資源的方法。通常一個(gè)數(shù)據(jù)庫(kù)就是一個(gè)資源管理器。
事務(wù)管理器(TM):協(xié)調(diào)者。分配標(biāo)識(shí)符,監(jiān)視事務(wù)的進(jìn)度,并負(fù)責(zé)事務(wù)完成和故障恢復(fù)。
應(yīng)用程序(AP):發(fā)起者。定義事務(wù)的邊界,制定全局事務(wù)中的操作。
?
整個(gè)過(guò)程分為2個(gè)階段:準(zhǔn)備(prepare)和提交(commit),prepare前需要先執(zhí)行對(duì)應(yīng)的DML操作。
?

?
Mysql XA事務(wù)語(yǔ)句:
注:執(zhí)行前需要先生成全局唯一的xidXA {START|BEGIN} xid [JOIN|RESUME]DML操作XA END xid [SUSPEND [FOR MIGRATE]]XA PREPARE xid //1.準(zhǔn)備XA COMMIT xid [ONE PHASE] //2.提交XA ROLLBACK xidXA RECOVER
簡(jiǎn)單的事例:
//準(zhǔn)備前階段XA START 'xatest';Query OK, 0 rows affected (0.00 sec)mysql> INSERT INTO mytable (i) VALUES(10);Query OK, 1 row affected (0.04 sec)mysql> XA END 'xatest';Query OK, 0 rows affected (0.00 sec)//準(zhǔn)備XA PREPARE 'xatest';Query OK, 0 rows affected (0.00 sec)//提交XA COMMIT 'xatest';Query OK, 0 rows affected (0.00 sec)
?XA看起來(lái)很美好,但還是存在一些問(wèn)題:
commit丟失/超時(shí)導(dǎo)致數(shù)據(jù)不一致。A commit成功,B commit時(shí)網(wǎng)絡(luò)超時(shí)導(dǎo)致數(shù)據(jù)庫(kù)未收到commit請(qǐng)求。
性能低。所有事務(wù)參與者在等待其它參與者響應(yīng)的時(shí)候都處于同步阻塞狀態(tài)。
主備數(shù)據(jù)不一致。
協(xié)調(diào)者單點(diǎn)故障,在任意階段協(xié)調(diào)者發(fā)生故障都會(huì)導(dǎo)致分布式事務(wù)無(wú)法進(jìn)行。
?
3.2 3PC
3PC通過(guò)在參與者加入超時(shí)機(jī)制解決2PC中的協(xié)調(diào)者單點(diǎn)帶來(lái)的事務(wù)無(wú)法進(jìn)行的問(wèn)題,但是性能和一致性仍沒(méi)有解決。
?
3.3 TCC
?
這樣看下來(lái)強(qiáng)一致是很難做了,還是最終一致把。。。
?
TCC是通過(guò)最終一致來(lái)達(dá)到分布式事務(wù)的效果,即:在短時(shí)間內(nèi)無(wú)法保證一致性,但最終會(huì)一致。核心分為2個(gè)階段:1.Try ?2.Confirm or Cancel。先嘗試(Try)操作數(shù)據(jù),如果都成功則全部確認(rèn)(Confirm)該修改,如果有任意一個(gè)嘗試失敗,則全部取消(Cancel)。簡(jiǎn)單點(diǎn)說(shuō)就是增加了中間態(tài)和可回改能力。
?

?
通過(guò)重試機(jī)制保障Confirm和Cancel一定能成功。TCC解決了性能問(wèn)題,但是業(yè)務(wù)系統(tǒng)想要實(shí)現(xiàn)對(duì)業(yè)務(wù)代碼的侵入性很大,可以學(xué)習(xí)一下阿里GTS是如何做的。
?
3.4 事務(wù)管理器
在實(shí)際運(yùn)用中事務(wù)管理器(TM)一般是由應(yīng)用程序(AP)兼職實(shí)現(xiàn)的,這樣對(duì)業(yè)務(wù)代碼侵入性大,最好是把TM做成中間層。接下來(lái)詳細(xì)看一下TM的職責(zé):協(xié)調(diào)者。分配標(biāo)識(shí)符,監(jiān)視事務(wù)的進(jìn)度,并負(fù)責(zé)事務(wù)完成和故障恢復(fù)。TM需要具備持久化能力才能完成監(jiān)控、故障恢復(fù)和協(xié)調(diào),整個(gè)協(xié)調(diào)過(guò)程可以分為3個(gè)階段:
準(zhǔn)備階段:向TM注冊(cè)全局事務(wù)并獲取到全局唯一XID,這一步需要定義好事務(wù)的范圍。
執(zhí)行階段:應(yīng)用程序AP執(zhí)行業(yè)務(wù)功能操作自己的RM,全部執(zhí)行完畢后向TM commit,如果無(wú)失敗則整個(gè)事務(wù)成功。
確認(rèn)/回滾階段:如果第2步出現(xiàn)異常會(huì)觸發(fā)該階段,TM咨詢各個(gè)AP對(duì)應(yīng)操作是否成功,如果成功則commit,如果失敗則調(diào)用AP進(jìn)行rollback。
?
下面簡(jiǎn)單介紹幾種實(shí)現(xiàn)方式。
3.4.1 本地事務(wù)管理器
對(duì)于簡(jiǎn)單的業(yè)務(wù)可能只要保障2個(gè)數(shù)據(jù)庫(kù)之間的一致,這樣在本地實(shí)現(xiàn)事務(wù)管理器比較快成本也不高。
?

?
1. 在做業(yè)務(wù)邏輯之前把對(duì)應(yīng)事件添加到本地event表中(記錄訂正時(shí)所需要的關(guān)鍵數(shù)據(jù))
2. 執(zhí)行業(yè)務(wù)邏輯
1.本地業(yè)務(wù)邏輯,操作表數(shù)據(jù)等等
2.RPC調(diào)用其他服務(wù)
3.修改event狀態(tài)為確認(rèn)
4. 如果第2步不成功,則event狀態(tài)還是預(yù)提交,通過(guò)定時(shí)任務(wù)撈取再執(zhí)行訂正邏輯
5. 檢查業(yè)務(wù)邏輯中的操作是否完成,如未完成則執(zhí)行訂正邏輯
1.本地業(yè)務(wù)邏輯是否執(zhí)行成功
2.RPC調(diào)用其他服務(wù)是否執(zhí)行成功
6.返回訂正結(jié)果
1.成功則修改event為確認(rèn)提交
2.未成功則不操作,等待輪詢訂正
3.4.2 外部事務(wù)管理器
阿里GTS:https://help.aliyun.com/document_detail/157850.html?
3.5 DB和MQ之間的一致性
?
在實(shí)際場(chǎng)景中,業(yè)務(wù)系統(tǒng)對(duì)本地DB數(shù)據(jù)變更后會(huì)廣播對(duì)應(yīng)的消息,消費(fèi)者消費(fèi)消息做自己的業(yè)務(wù)邏輯,按正常邏輯消息會(huì)在數(shù)據(jù)庫(kù)變更后發(fā)出,如果消息發(fā)送超時(shí)且失敗那么DB和MQ之間就產(chǎn)生了不一致問(wèn)題,如何解決呢?使用可靠消息來(lái)解決,核心邏輯保證消息從投遞到消費(fèi)的過(guò)程中不會(huì)丟失:生產(chǎn)者confirm、消費(fèi)者ack和持久化。理論上只要使用生產(chǎn)者確認(rèn)機(jī)制即可,但是不使用消費(fèi)者ack則沒(méi)有意義。消費(fèi)者需要開(kāi)啟手動(dòng)ack同時(shí)做到冪等消費(fèi),MQ需要通過(guò)將exchange、queue和message進(jìn)行持久化來(lái)保證消息不丟失,生產(chǎn)者則需要通過(guò)確認(rèn)機(jī)制保證消息一定投遞到MQ種,接下來(lái)重點(diǎn)講解一下生產(chǎn)者確認(rèn)機(jī)制。
?

confirm機(jī)制是在消息投遞到所有匹配的queue之后發(fā)送確認(rèn)消息給生產(chǎn)者,這樣生產(chǎn)者就知道消息投遞成功,但是由于消息是在DB操作之后發(fā)出的,生產(chǎn)者必須增加記錄表來(lái)記錄消息投遞狀態(tài),如果投遞成功就在收到確認(rèn)消息時(shí)把記錄標(biāo)記為投遞成功,如果長(zhǎng)時(shí)間未收到確認(rèn)消息則大概率是消息丟失了,再定時(shí)重新投遞,這樣就可以保證消息最終一定能投遞成功。核心邏輯其實(shí)就是通過(guò)全局事務(wù)ID來(lái)標(biāo)識(shí)DB操作和MQ消息是在一個(gè)分布式事務(wù)中的。
?
疑點(diǎn):在學(xué)習(xí)RabbitMQ confirm機(jī)制時(shí)發(fā)現(xiàn)生產(chǎn)者接收到的確認(rèn)消息只有消息ID,這個(gè)消息ID不能作為全局事務(wù)ID,所以無(wú)法解決DB和MQ之間的一致性問(wèn)題,后續(xù)再看看其它MQ產(chǎn)品是怎么做的。
?
上面的方式需要業(yè)務(wù)系統(tǒng)維護(hù)消息狀態(tài),這部分可以交給中間件來(lái)實(shí)現(xiàn),實(shí)現(xiàn)邏輯會(huì)變得不一樣。
?

?
預(yù)投遞的消息不會(huì)分發(fā)到queue中,只有在接收到確認(rèn)投遞的請(qǐng)求后才會(huì)進(jìn)行投遞,如果確認(rèn)操作因?yàn)榫W(wǎng)絡(luò)異常失敗了,MQ在過(guò)一段時(shí)間之后主動(dòng)詢問(wèn)業(yè)務(wù)系統(tǒng)該消息是否可投遞(失敗不斷重試),這樣就能在異常時(shí)做到最終一致,不過(guò)依賴MQ產(chǎn)品的能力。
3.6 DB和緩存之間的一致性
DB和緩存之間同樣也存在不一致問(wèn)題,先寫DB再寫緩存如果緩存寫失敗就不一致了,同樣的需要重試更新緩存來(lái)做最終一致。方法可以參考“2.重試”部分,如果一定要保證重試不丟失可以用可靠消息或本地task表來(lái)記錄重試操作,有條件的可以使用DB DRC消息對(duì)業(yè)務(wù)的侵入會(huì)小一些。需要注意的是同步更新和異步更新同時(shí)使用時(shí)可能會(huì)產(chǎn)生更新覆蓋的問(wèn)題,加上毫秒級(jí)的時(shí)間戳或版本號(hào)來(lái)丟棄舊的更新。
4. 兜底核對(duì)
雖然有了分布式事務(wù),但是在實(shí)際場(chǎng)景中可能會(huì)因?yàn)閎ug導(dǎo)致數(shù)據(jù)不一致,這時(shí)需要兜底來(lái)做最后一道防線,通過(guò)定時(shí)核對(duì)數(shù)據(jù)是否一致,如不一致手動(dòng)/自動(dòng)進(jìn)行訂正。
4.1 系統(tǒng)自核對(duì)
如果系統(tǒng)數(shù)量和數(shù)據(jù)量不多的情況下可以由業(yè)務(wù)系統(tǒng)自行核對(duì),通過(guò)發(fā)送延遲消息自消費(fèi)或監(jiān)聽(tīng)其他系統(tǒng)消息做相關(guān)數(shù)據(jù)核對(duì)并進(jìn)行訂正。
?

4.2 搭建核對(duì)系統(tǒng)
在核對(duì)工作繁多的情況下,由業(yè)務(wù)系統(tǒng)自己核對(duì)會(huì)存在很多耦合,這時(shí)可以選擇搭建獨(dú)立的核對(duì)系統(tǒng)進(jìn)行核對(duì)和訂正。
?
1.監(jiān)聽(tīng)drc消息
?

?
2.監(jiān)聽(tīng)業(yè)務(wù)消息
?

參考:
阿里GTS:https://help.aliyun.com/document_detail/157850.html?spm=a2c4g.11186623.6.554.47ae4df4Zy6hBI
餓了么DRC:https://zhuanlan.zhihu.com/p/34958596
Mysql XA:https://dev.mysql.com/doc/refman/8.0/en/xa-statements.html

版權(quán)聲明:
文章不錯(cuò)?點(diǎn)個(gè)【在看】吧!??




