分布式事務(wù)詳解

目前可找到很多成熟的開(kāi)源分布式事務(wù)解決方案,比較典型的方案如阿里的fescar,螞蟻金服的Seata,LCN(https://github.com/codingapi/tx-lcn)的2pc型無(wú)侵入事務(wù)。還有如TCC型事務(wù)實(shí)現(xiàn)hmily(https://github.com/yu199195/hmily)、tcc-transaction(https://github.com/changmingxie/tcc-transaction)等
Seata: https://github.com/seata/seata
fescar:https://github.com/alibaba/fescar
tcc-transaction: https://github.com/changmingxie/tcc-transaction
Hmily: https://github.com/yu199195/hmily
LCN: https://github.com/codingapi/tx-lcn

創(chuàng)新互聯(lián)主要業(yè)務(wù)有網(wǎng)站營(yíng)銷(xiāo)策劃、成都網(wǎng)站建設(shè)、成都做網(wǎng)站、微信公眾號(hào)開(kāi)發(fā)、小程序設(shè)計(jì)、H5技術(shù)、程序開(kāi)發(fā)等業(yè)務(wù)。一次合作終身朋友,是我們奉行的宗旨;我們不僅僅把客戶當(dāng)客戶,還把客戶視為我們的合作伙伴,在開(kāi)展業(yè)務(wù)的過(guò)程中,公司還積累了豐富的行業(yè)經(jīng)驗(yàn)、營(yíng)銷(xiāo)型網(wǎng)站建設(shè)資源和合作伙伴關(guān)系資源,并逐漸建立起規(guī)范的客戶服務(wù)和保障體系。 

分布式事務(wù)有個(gè)注明的CAP理論:C,A,P無(wú)法同時(shí)全部滿足,最多滿足兩個(gè)。Cassandra、Dynamo 等,默認(rèn)優(yōu)先選擇AP,弱化C;HBase、MongoDB 等,默認(rèn)優(yōu)先選擇CP,弱化A。
分布式事務(wù)詳解

BASE模型包含個(gè)三個(gè)元素:
BA:Basically Available,基本可用
S:Soft State,軟狀態(tài),狀態(tài)可以有一段時(shí)間不同步
E:Eventually Consistent,最終一致,最終數(shù)據(jù)是一致的就可以了,而不是時(shí)時(shí)保持強(qiáng)一致
BASE模型與ACID模型截然不同,滿足CAP理論,通過(guò)犧牲強(qiáng)一致性,獲得可用性,一般應(yīng)用在服務(wù)化系統(tǒng)的應(yīng)用層或者大數(shù)據(jù)處理系統(tǒng),通過(guò)達(dá)到最終一致性來(lái)盡量滿足業(yè)務(wù)的絕大部分需求。

分布式事務(wù)的目的是保障分布式存儲(chǔ)中數(shù)據(jù)一致性,而跨庫(kù)事務(wù)會(huì)遇到各種不可控制的問(wèn)題,如個(gè)別節(jié)點(diǎn)宕機(jī),像單機(jī)事務(wù)一樣的ACID是無(wú)法奢望的。

1、Two/Three Phase Commit

2PC,中文叫兩階段提交。在分布式系統(tǒng)中,每個(gè)節(jié)點(diǎn)雖然可以知曉自己的操作時(shí)成功或者失敗,卻無(wú)法知道其他節(jié)點(diǎn)的操作的成功或失敗。當(dāng)一個(gè)事務(wù)跨越多個(gè)節(jié)點(diǎn)時(shí),為了保持事務(wù)的ACID特性,需要引入一個(gè)作為協(xié)調(diào)者的組件來(lái)統(tǒng)一掌控所有節(jié)點(diǎn)(稱(chēng)作參與者)的操作結(jié)果并最終指示這些節(jié)點(diǎn)是否要把操作結(jié)果進(jìn)行真正的提交。 兩階段提交的算法如下:

第一階段:

協(xié)調(diào)者會(huì)問(wèn)所有的參與者結(jié)點(diǎn),是否可以執(zhí)行提交操作。
各個(gè)參與者開(kāi)始事務(wù)執(zhí)行的準(zhǔn)備工作:如:為資源上鎖,預(yù)留資源。
參與者響應(yīng)協(xié)調(diào)者,如果事務(wù)的準(zhǔn)備工作成功,則回應(yīng)“可以提交”,否則回應(yīng)“拒絕提交”。
第二階段:

如果所有的參與者都回應(yīng)“可以提交”,那么,協(xié)調(diào)者向所有的參與者發(fā)送“正式提交”的命令。參與者完成正式提交,并釋放所有資源,然后回應(yīng)“完成”,協(xié)調(diào)者收集各結(jié)點(diǎn)的“完成”回應(yīng)后結(jié)束這個(gè)Global Transaction。
如果有一個(gè)參與者回應(yīng)“拒絕提交”,那么,協(xié)調(diào)者向所有的參與者發(fā)送“回滾操作”,并釋放所有資源,然后回應(yīng)“回滾完成”,協(xié)調(diào)者收集各結(jié)點(diǎn)的“回滾”回應(yīng)后,取消這個(gè)Global Transaction。
兩段提交最大的問(wèn)題就是第3)項(xiàng),如果第一階段完成后,參與者在第二階沒(méi)有收到?jīng)Q策,那么數(shù)據(jù)結(jié)點(diǎn)會(huì)進(jìn)入“不知所措”的狀態(tài),這個(gè)狀態(tài)會(huì)block住整個(gè)事務(wù)。也就是說(shuō),協(xié)調(diào)者Coordinator對(duì)于事務(wù)的完成非常重要,Coordinator的可用性是個(gè)關(guān)鍵。

因些,我們引入三段提交,三段提交在Wikipedia上的描述如下,他把二段提交的第一個(gè)段break成了兩段:詢(xún)問(wèn),然后再鎖資源。最后真正提交。三段提交的核心理念是:在詢(xún)問(wèn)的時(shí)候并不鎖定資源,除非所有人都同意了,才開(kāi)始鎖資源。但三階段提交也存在一些缺陷,要徹底從協(xié)議層面避免數(shù)據(jù)不一致,可以采用Paxos或者Raft 算法。

目前兩階段提交、三階段提交存在如下的局限性,并不適合在微服務(wù)架構(gòu)體系下使用:

所有的操作必須是事務(wù)性資源(比如數(shù)據(jù)庫(kù)、消息隊(duì)列、EJB組件等),存在使用局限性(微服務(wù)架構(gòu)下多數(shù)使用HTTP協(xié)議),比較適合傳統(tǒng)的單體應(yīng)用;

由于是強(qiáng)一致性,資源需要在事務(wù)內(nèi)部等待,性能影響較大,吞吐率不高,不適合高并發(fā)與高性能的業(yè)務(wù)場(chǎng)景;

2、Try Confirm Cancel(TCC)

一個(gè)完整的TCC業(yè)務(wù)由一個(gè)主業(yè)務(wù)服務(wù)和若干個(gè)從業(yè)務(wù)服務(wù)組成,主業(yè)務(wù)服務(wù)發(fā)起并完成整個(gè)業(yè)務(wù)活動(dòng),TCC模式要求從服務(wù)提供三個(gè)接口:Try、Confirm、Cancel。

Try:完成所有業(yè)務(wù)檢查,預(yù)留必須業(yè)務(wù)資源。
Confirm:真正執(zhí)行業(yè)務(wù),不作任何業(yè)務(wù)檢查;只使用Try階段預(yù)留的業(yè)務(wù)資源;Confirm操作滿足冪等性。

Cancel:釋放Try階段預(yù)留的業(yè)務(wù)資源;Cancel操作滿足冪等性。

整個(gè)TCC業(yè)務(wù)分成兩個(gè)階段完成:

分布式事務(wù)詳解

第一階段:主業(yè)務(wù)服務(wù)分別調(diào)用所有從業(yè)務(wù)的try操作,并在活動(dòng)管理器中登記所有從業(yè)務(wù)服務(wù)。當(dāng)所有從業(yè)務(wù)服務(wù)的try操作都調(diào)用成功或者某個(gè)從業(yè)務(wù)服務(wù)的try操作失敗,進(jìn)入第二階段。

第二階段:活動(dòng)管理器根據(jù)第一階段的執(zhí)行結(jié)果來(lái)執(zhí)行confirm或cancel操作。如果第一階段所有try操作都成功,則活動(dòng)管理器調(diào)用所有從業(yè)務(wù)活動(dòng)的confirm操作。否則調(diào)用所有從業(yè)務(wù)服務(wù)的cancel操作。

與2PC比較:

位于業(yè)務(wù)服務(wù)層而非資源層。
沒(méi)有單獨(dú)的準(zhǔn)備(prepare)階段,Try操作兼?zhèn)滟Y源操作與準(zhǔn)備能力。
Try操作可以靈活選擇業(yè)務(wù)資源的鎖定粒度。
開(kāi)發(fā)成本較高。
缺點(diǎn):

Canfirm和Cancel的冪等性很難保證。
這種方式缺點(diǎn)比較多,通常在復(fù)雜場(chǎng)景下是不推薦使用的,除非是非常簡(jiǎn)單的場(chǎng)景,非常容易提供回滾Cancel,而且依賴(lài)的服務(wù)也非常少的情況。
這種實(shí)現(xiàn)方式會(huì)造成代碼量龐大,耦合性高。而且非常有局限性,因?yàn)橛泻芏嗟臉I(yè)務(wù)是無(wú)法很簡(jiǎn)單的實(shí)現(xiàn)回滾的,如果串行的服務(wù)很多,回滾的成本實(shí)在太高。
3、異步確保最終一致性

核心思想:

eBay 的架構(gòu)師Dan Pritchett,曾在一篇解釋BASE 原理的論文《Base:An Acid Alternative》中提到一個(gè)eBay 分布式系統(tǒng)一致性問(wèn)題的解決方案。它的核心思想是將需要分布式處理的任務(wù)通過(guò)消息或者日志的方式來(lái)異步執(zhí)行,消息或日志可以存到本地文件、數(shù)據(jù)庫(kù)或消息隊(duì)列,再通過(guò)業(yè)務(wù)規(guī)則進(jìn)行失敗重試,它要求各服務(wù)的接口是冪等的。
本地消息表

其基本的設(shè)計(jì)思想是將遠(yuǎn)程分布式事務(wù)拆分成一系列的本地事務(wù)。如果不考慮性能及設(shè)計(jì)優(yōu)雅,借助關(guān)系型數(shù)據(jù)庫(kù)中的表即可實(shí)現(xiàn)。

舉個(gè)經(jīng)典的跨行轉(zhuǎn)賬的例子來(lái)描述。

第一步偽代碼如下,扣款100,通過(guò)本地事務(wù)保證了憑證消息插入到消息表中:

begin transaction:
  update User set account = account - 100 where userId = 'A'
  insert into message(msgId, userId, amount, status) values('123','A', 100, 1)
commit transaction

第二步,通知對(duì)方銀行賬戶上加100了。那問(wèn)題來(lái)了,如何通知到對(duì)方呢?

通常采用兩種方式:

采用時(shí)效性高的MQ,由對(duì)方訂閱消息并監(jiān)聽(tīng),有消息時(shí)自動(dòng)觸發(fā)事件。
采用定時(shí)輪詢(xún)掃描的方式,去檢查消息表的數(shù)據(jù)。
兩種方式其實(shí)各有利弊,僅僅依靠MQ,可能會(huì)出現(xiàn)通知失敗的問(wèn)題。而過(guò)于頻繁的定時(shí)輪詢(xún),效率也不是最佳的(90%是無(wú)用功)。所以,我們一般會(huì)把兩種方式結(jié)合起來(lái)使用。

解決了通知的問(wèn)題,又有新的問(wèn)題了。萬(wàn)一這消息有重復(fù)被消費(fèi),往用戶帳號(hào)上多加了錢(qián),那豈不是后果很?chē)?yán)重?其實(shí)我們可以消息消費(fèi)方也通過(guò)一個(gè)“消費(fèi)狀態(tài)表”來(lái)記錄消費(fèi)狀態(tài)。在執(zhí)行“加款”操作之前,檢測(cè)下該消息(提供標(biāo)識(shí))是否已經(jīng)消費(fèi)過(guò),消費(fèi)完成后,通過(guò)本地事務(wù)控制來(lái)更新這個(gè)“消費(fèi)狀態(tài)表”。這樣子就避免重復(fù)消費(fèi)的問(wèn)題:

get msgId = '123';
check if mgsId is in message_applied(msgId);
if not applied:
    begin transaction:
        update User set account = account + 100 where userId = 'B'
        insert into message_applied(msgId) values('123')
    commit transaction

上訴的方式是一種非常經(jīng)典的實(shí)現(xiàn),基本避免了分布式事務(wù),實(shí)現(xiàn)了“最終一致性”。但是,關(guān)系型數(shù)據(jù)庫(kù)的吞吐量和性能方面存在瓶頸,頻繁的讀寫(xiě)消息會(huì)給數(shù)據(jù)庫(kù)造成壓力。所以,在真正的高并發(fā)場(chǎng)景下,該方案也會(huì)有瓶頸和限制的。

MQ(非事務(wù)消息)

通常情況下,在使用非事務(wù)消息支持的MQ產(chǎn)品時(shí),我們很難將業(yè)務(wù)操作與對(duì)MQ的操作放在一個(gè)本地事務(wù)域中管理。還是以上述提到的“跨行轉(zhuǎn)賬”為例,我們很難保證在扣款完成之后對(duì)MQ投遞消息的操作就一定能成功。這樣一致性似乎很難保證。

我們來(lái)分析下可能的情況:

操作數(shù)據(jù)庫(kù)成功,向MQ中投遞消息也成功,皆大歡喜。
操作數(shù)據(jù)庫(kù)失敗,不會(huì)向MQ中投遞消息了。
操作數(shù)據(jù)庫(kù)成功,但是向MQ中投遞消息時(shí)失敗,向外拋出了異常,剛剛執(zhí)行的更新數(shù)據(jù)庫(kù)的操作將被回滾。
從上面分析的幾種情況來(lái)看,貌似問(wèn)題都不大的。那么我們來(lái)分析下消費(fèi)者端面臨的問(wèn)題:

消息出列后,消費(fèi)者對(duì)應(yīng)的業(yè)務(wù)操作要執(zhí)行成功。如果業(yè)務(wù)執(zhí)行失敗,消息不能失效或者丟失。需要保證消息與業(yè)務(wù)操作一致。
盡量避免消息重復(fù)消費(fèi)。如果重復(fù)消費(fèi),也不能因此影響業(yè)務(wù)結(jié)果。
如何保證消息與業(yè)務(wù)操作一致,不丟失?

主流的MQ產(chǎn)品都具有持久化消息的功能。如果消費(fèi)者宕機(jī)或者消費(fèi)失敗,都可以執(zhí)行重試機(jī)制的(有些MQ可以自定義重試次數(shù))。

如何避免消息被重復(fù)消費(fèi)造成的問(wèn)題?

保證消費(fèi)者調(diào)用業(yè)務(wù)的服務(wù)接口的冪等性。
通過(guò)消費(fèi)日志或者類(lèi)似狀態(tài)表來(lái)記錄消費(fèi)狀態(tài),便于判斷(建議在業(yè)務(wù)上自行實(shí)現(xiàn),而不依賴(lài)MQ產(chǎn)品提供該特性)。
這種方式比較常見(jiàn),性能和吞吐量是優(yōu)于使用關(guān)系型數(shù)據(jù)庫(kù)消息表的方案。如果MQ自身和業(yè)務(wù)都具有高可用性,理論上是可以滿足大部分的業(yè)務(wù)場(chǎng)景的。不過(guò)在沒(méi)有充分測(cè)試的情況下,不建議在交易業(yè)務(wù)中直接使用。

MQ(事務(wù)消息)

舉個(gè)例子,Bob向Smith轉(zhuǎn)賬,那我們到底是先發(fā)送消息,還是先執(zhí)行扣款操作?

好像都可能會(huì)出問(wèn)題。如果先發(fā)消息,扣款操作失敗,那么Smith的賬戶里面會(huì)多出一筆錢(qián)。反過(guò)來(lái),如果先執(zhí)行扣款操作,后發(fā)送消息,那有可能扣款成功了但是消息沒(méi)發(fā)出去,Smith收不到錢(qián)。除了上面介紹的通過(guò)異常捕獲和回滾的方式外,還有沒(méi)有其他的思路呢?

下面以阿里巴巴的RocketMQ中間件為例,分析下其設(shè)計(jì)和實(shí)現(xiàn)思路。

RocketMQ第一階段發(fā)送Prepared消息時(shí),會(huì)拿到消息的地址,第二階段執(zhí)行本地事物,第三階段通過(guò)第一階段拿到的地址去訪問(wèn)消息,并修改狀態(tài)。細(xì)心的讀者可能又發(fā)現(xiàn)問(wèn)題了,如果確認(rèn)消息發(fā)送失敗了怎么辦?RocketMQ會(huì)定期掃描消息集群中的事物消息,這時(shí)候發(fā)現(xiàn)了Prepared消息,它會(huì)向消息發(fā)送者確認(rèn),Bob的錢(qián)到底是減了還是沒(méi)減呢?如果減了是回滾還是繼續(xù)發(fā)送確認(rèn)消息呢?RocketMQ會(huì)根據(jù)發(fā)送端設(shè)置的策略來(lái)決定是回滾還是繼續(xù)發(fā)送確認(rèn)消息。這樣就保證了消息發(fā)送與本地事務(wù)同時(shí)成功或同時(shí)失敗。如下圖:

分布式事務(wù)詳解

各大知名的電商平臺(tái)和互聯(lián)網(wǎng)公司,幾乎都是采用類(lèi)似的設(shè)計(jì)思路來(lái)實(shí)現(xiàn)“最終一致性”的。這種方式適合的業(yè)務(wù)場(chǎng)景廣泛,而且比較可靠。不過(guò)這種方式技術(shù)實(shí)現(xiàn)的難度比較大。目前主流的開(kāi)源MQ(ActiveMQ、RabbitMQ、Kafka)均未實(shí)現(xiàn)對(duì)事務(wù)消息的支持,所以需二次開(kāi)發(fā),可參考RocketMQ的事務(wù)消息(transactional message)。

總結(jié):

閱讀了不少這方面的文章,在此基礎(chǔ)上,總結(jié)一下分布式事務(wù)一致性的解決方案。分布式系統(tǒng)的事務(wù)一致性本身就是一個(gè)技術(shù)難題,目前沒(méi)有一種很簡(jiǎn)單很完美的方案能夠應(yīng)對(duì)所有場(chǎng)景。分布式系統(tǒng)的一個(gè)難點(diǎn)就是因?yàn)椤熬W(wǎng)絡(luò)通信的不可靠”,只能通過(guò)“確認(rèn)機(jī)制”、“重試機(jī)制”、“補(bǔ)償機(jī)制”等各方面來(lái)解決一些問(wèn)題。在綜合考慮可用性、性能、實(shí)現(xiàn)復(fù)雜度等各方面的情況上,比較好的選擇是“異步確保最終一致性”,只是具體實(shí)現(xiàn)方式上有一些差異。

參考文獻(xiàn):
https://www.cnblogs.com/luxiaoxun/p/8832915.html
https://www.cnblogs.com/lori/p/9318892.html
分布式系統(tǒng)的事務(wù)處理
https://coolshell.cn/articles/10910.html
用消息隊(duì)列和消息應(yīng)用狀態(tài)表來(lái)消除分布式事務(wù)
https://my.oschina.net/picasso/blog/35306
常用的分布式事務(wù)解決方案介紹有多少種?
https://www.zhihu.com/question/64921387/answer/225784480
分布式事務(wù):不過(guò)是在一致性、吞吐量和復(fù)雜度之間,做一個(gè)選擇
https://mp.weixin.qq.com/s?__biz=MjM5MDE0Mjc4MA==&mid=2650994325&idx=1&sn=afe66f9cf65ec61aaaf8422a12618fb2

網(wǎng)頁(yè)題目:分布式事務(wù)詳解
網(wǎng)站地址:http://muchs.cn/article30/iidiso.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供小程序開(kāi)發(fā)、品牌網(wǎng)站設(shè)計(jì)、響應(yīng)式網(wǎng)站、虛擬主機(jī)服務(wù)器托管

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

小程序開(kāi)發(fā)