前言
上一篇文章《就這套鹅?分布式 ID 發(fā)號(hào)器實(shí)戰(zhàn)》之后偷拔,我朋友輝哥在后臺(tái)留言讓靚仔聊聊分布式事務(wù)洪囤,既然輝哥都開(kāi)口了昆稿,那必須得滿(mǎn)足啊炼蹦,安排残腌!
溫馨提示:文章很干烁登,請(qǐng)多喝水
什么是分布式事務(wù)
什么是事務(wù)想必大多數(shù)朋友應(yīng)該都很清楚了,不清楚的可以看前面的文章《就這易阳?一篇文章讓你讀懂 Spring 事務(wù)》附较。
分布式事務(wù)就是指事務(wù)的參與者、支持事務(wù)的服務(wù)器潦俺、資源服務(wù)器以及事務(wù)管理器分別位于不同的分布式系統(tǒng)的不同節(jié)點(diǎn)之上拒课。
簡(jiǎn)單來(lái)說(shuō),就是一個(gè)大的操作由 N 個(gè)小操作組成事示,這些小的操作分布在不同的服務(wù)器上早像,且屬于不同的應(yīng)用,分布式事務(wù)需要保證這些小操作要么全部成功很魂,要么全部失敗扎酷。比如存在一個(gè)訂單的微服務(wù),一個(gè)庫(kù)存的微服務(wù),當(dāng)訂單完成需要同步減少庫(kù)存,這時(shí)候就要在事務(wù)上確保完整和一致零院。
相關(guān)理論
關(guān)于事務(wù)的特性(ACID)和隔離級(jí)別這里就不再重復(fù)介紹了铛漓,可以看前面的文章,這里著重介紹下兩個(gè)新的知識(shí):CAP 理論和 BASE 理論。
CAP 理論
- 一致性(Consistency):在分布式系統(tǒng)完成某寫(xiě)操作后任何讀操作,都應(yīng)該獲取到該寫(xiě)操作寫(xiě)入的那個(gè)最新的值。相當(dāng)于要求分布式系統(tǒng)中的各節(jié)點(diǎn)時(shí)時(shí)刻刻保持?jǐn)?shù)據(jù)的一致性荐糜。
- 可用性(Availability): 一直可以正常的做讀寫(xiě)操作。簡(jiǎn)單而言就是客戶(hù)端一直可以正常訪(fǎng)問(wèn)并得到系統(tǒng)的正常響應(yīng)。用戶(hù)角度來(lái)看就是不會(huì)出現(xiàn)系統(tǒng)操作失敗或者訪(fǎng)問(wèn)超時(shí)等問(wèn)題暴氏。
- 分區(qū)容錯(cuò)性(PartitionTolerance):指的分布式系統(tǒng)中的某個(gè)節(jié)點(diǎn)或者網(wǎng)絡(luò)分區(qū)出現(xiàn)了故障的時(shí)候延塑,整個(gè)系統(tǒng)仍然能對(duì)外提供滿(mǎn)足一致性和可用性的服務(wù)。也就是說(shuō)部分故障不影響整體使用答渔。事實(shí)上我們?cè)谠O(shè)計(jì)分布式系統(tǒng)是都會(huì)考慮到 bug关带、硬件、網(wǎng)絡(luò)等各種原因造成的故障沼撕,所以即使部分節(jié)點(diǎn)或者網(wǎng)絡(luò)出現(xiàn)故障宋雏,我們要求整個(gè)系統(tǒng)還是要繼續(xù)使用的
CAP 是一個(gè)已經(jīng)被證實(shí)的理論,在分布式系統(tǒng)中最多只能同時(shí)滿(mǎn)足這三項(xiàng)中的兩項(xiàng)务豺,而分區(qū)容錯(cuò)性是分布式系統(tǒng)必須滿(mǎn)足的磨总,所以在分布式系統(tǒng)中常見(jiàn)的組合就是 CP 和 AP
- CP:放棄可用性,注重一致性和分區(qū)容錯(cuò)性笼沥,其實(shí)這就是所謂的強(qiáng)一致性蚪燕,可能在銀行跨行轉(zhuǎn)賬這種強(qiáng)一致業(yè)務(wù)場(chǎng)景才會(huì)用到,具體得根據(jù)業(yè)務(wù)場(chǎng)景做取舍敬拓。
- AP:放棄強(qiáng)一致性邻薯,注重可用性和分區(qū)容錯(cuò)性,這是現(xiàn)在絕大多數(shù)分布式業(yè)務(wù)場(chǎng)景的選擇乘凸,只要最后能保證最終一致性( BASE 理論)即可。
BASE 理論
基本可用(Basically Available):基本可用是指分布式系統(tǒng)在出現(xiàn)故障的時(shí)候累榜,允許損失部分可用性营勤,即保證核心可用。電商大促時(shí)壹罚,為了應(yīng)對(duì)訪(fǎng)問(wèn)量激增葛作,部分用戶(hù)可能會(huì)被引導(dǎo)到降級(jí)頁(yè)面,服務(wù)層也可能只提供降級(jí)服務(wù)猖凛。這就是損失部分可用性的體現(xiàn)赂蠢。
-
軟狀態(tài)(Soft State):軟狀態(tài)是指允許系統(tǒng)存在中間狀態(tài),而該中間狀態(tài)不會(huì)影響系統(tǒng)整體可用性辨泳。分布式存儲(chǔ)中一般一份數(shù)據(jù)至少會(huì)有三個(gè)副本虱岂,允許不同節(jié)點(diǎn)間副本同步的延時(shí)就是軟狀態(tài)的體現(xiàn)。MySQL Replication 的異步復(fù)制也是一種體現(xiàn)菠红。
最終一致性(Eventual Consistency):最終一致性是指系統(tǒng)中的所有數(shù)據(jù)副本經(jīng)過(guò)一定時(shí)間后第岖,最終能夠達(dá)到一致的狀態(tài)。弱一致性和強(qiáng)一致性相反试溯,最終一致性是弱一致性的一種特殊情況蔑滓。
常見(jiàn)解決方案
1、兩階段提交
兩階段提交(Two-phaseCommit),簡(jiǎn)稱(chēng)為 2PC键袱,兩階段提交是一種強(qiáng)一致性設(shè)計(jì)燎窘,它引入一個(gè)事務(wù)協(xié)調(diào)者的角色來(lái)協(xié)調(diào)管理各個(gè)參與者(也可稱(chēng)之為各本地資源)的提交和回滾。
所謂的兩個(gè)階段是指:第一階段:準(zhǔn)備階段(投票階段)和第二階段:提交階段(執(zhí)行階段)蹄咖。
準(zhǔn)備階段(Prepare Phase):首先協(xié)調(diào)器會(huì)向所有的參與者發(fā)送準(zhǔn)備提交或者取消提交的請(qǐng)求荠耽,然后會(huì)收集參與者的決策。
提交階段(Commit Phase):協(xié)調(diào)者會(huì)收集所有參與者的決策信息比藻,當(dāng)且僅當(dāng)所有的參與者向協(xié)調(diào)器發(fā)送確認(rèn)消息時(shí)協(xié)調(diào)器才會(huì)提交請(qǐng)求铝量,否則執(zhí)行回滾或者取消請(qǐng)求。
2PC 存在的問(wèn)題:
- 同步阻塞:所有的參與者都是事務(wù)同步阻塞型的银亲。當(dāng)參與者占有公共資源時(shí)慢叨,其他第三方節(jié)點(diǎn)訪(fǎng)問(wèn)公共資源不得不處于阻塞狀態(tài)。
- 單點(diǎn)故障:一旦協(xié)調(diào)者發(fā)生故障务蝠,系統(tǒng)不可用拍谐。
- 數(shù)據(jù)不一致:當(dāng)協(xié)調(diào)者發(fā)送 commit 之后,有的參與者收到 commit 消息馏段,事務(wù)執(zhí)行成功轩拨,有的沒(méi)有收到,處于阻塞狀態(tài)院喜,這段時(shí)間會(huì)產(chǎn)生數(shù)據(jù)不一致亡蓉。
- 不確定性:當(dāng)協(xié)調(diào)者發(fā)送 commit 之后,并且此時(shí)只有一個(gè)參與者收到了 commit喷舀,那么當(dāng)該參與者與協(xié)調(diào)器同時(shí)宕機(jī)之后砍濒,重新選舉的協(xié)調(diào)器無(wú)法確定該條消息是否提交成功。
2PC 的優(yōu)勢(shì)在于對(duì)業(yè)務(wù)沒(méi)有侵入硫麻,可以利用數(shù)據(jù)庫(kù)自身機(jī)制來(lái)進(jìn)行事務(wù)的提交和回滾爸邢。
常見(jiàn)的基于 2PC 的具體落地方案有:JTA(XA 規(guī)范) 和 Seata( AT 模式)。
2拿愧、三階段提交
三階段提交(Three-phase commit)杠河,簡(jiǎn)稱(chēng)為 3PC,是 2PC 的改進(jìn)版本浇辜。同時(shí)在協(xié)調(diào)者和參與者都引入了超時(shí)機(jī)制券敌,還在 2PC 中的準(zhǔn)備階段和提交階段中間增加了一個(gè)預(yù)提交階段。
- 準(zhǔn)備階段(CanCommit):協(xié)調(diào)者向各個(gè)參與者發(fā)送請(qǐng)求奢赂,詢(xún)問(wèn)是否可以執(zhí)行事務(wù)陪白,但并不執(zhí)行事務(wù)。
- 預(yù)提交階段(PreCommit):如果從協(xié)調(diào)者得到的反饋是滿(mǎn)足執(zhí)行條件膳灶,那么就發(fā)送預(yù)提交請(qǐng)求咱士,并開(kāi)始執(zhí)行事務(wù)立由;如果從協(xié)調(diào)者得到的反饋是不滿(mǎn)足執(zhí)行條件或者超時(shí),則發(fā)送事務(wù)中斷請(qǐng)求序厉。
- 提交階段(DoCommit):如果預(yù)提交階段發(fā)送的是預(yù)提交請(qǐng)求锐膜,那么正常提交事務(wù);如果預(yù)提交階段發(fā)送的是事務(wù)中斷請(qǐng)求弛房,那么直接中斷事務(wù)道盏。
相對(duì)于 2PC,3PC 主要解決的單點(diǎn)故障問(wèn)題文捶,并減少阻塞荷逞,因?yàn)橐坏﹨⑴c者無(wú)法及時(shí)收到來(lái)自協(xié)調(diào)者的信息之后,他會(huì)默認(rèn)執(zhí)行 commit粹排。而不會(huì)一直持有事務(wù)資源并處于阻塞狀態(tài)种远。但是這種機(jī)制也會(huì)導(dǎo)致數(shù)據(jù)一致性問(wèn)題,因?yàn)橥缍捎诰W(wǎng)絡(luò)原因坠敷,協(xié)調(diào)者發(fā)送的中斷響應(yīng)沒(méi)有及時(shí)被參與者接收到,那么參與者在等待超時(shí)之后執(zhí)行了 commit 操作射富。這樣就和其他接到中斷命令并執(zhí)行回滾的參與者之間存在數(shù)據(jù)不一致的情況膝迎。而且 3PC 整體的交互過(guò)程更長(zhǎng),性能也會(huì)有所下降胰耗。
3PC 目前似乎只存在于理論限次,還沒(méi)有具體落地方案。
3宪郊、TCC
2PC 和 3PC 都是依賴(lài)于數(shù)據(jù)庫(kù)的事務(wù)提交和回滾掂恕,但是有時(shí)候很多業(yè)務(wù)并不僅僅只涉及到數(shù)據(jù)庫(kù),可能還會(huì)發(fā)送短息弛槐、消息等等,而 TCC 就是屬于業(yè)務(wù)層面或者說(shuō)是應(yīng)用層面的分布式事務(wù)依啰。
TCC 方案分為T(mén)ry-Confirm-Cancel三個(gè)階段乎串,屬于補(bǔ)償性分布式事務(wù)。
- Try 階段:完成所有業(yè)務(wù)檢查(一致性)速警,預(yù)留業(yè)務(wù)資源(準(zhǔn)隔離性)
- Confirm 階段:確認(rèn)執(zhí)行業(yè)務(wù)操作叹誉,不再做任何業(yè)務(wù)檢查, 只使用Try階段預(yù)留的業(yè)務(wù)資源闷旧。
- Cancel 階段:取消Try階段預(yù)留的業(yè)務(wù)資源长豁。
有的朋友可能會(huì)問(wèn)了,Try 成功了會(huì)執(zhí)行 Confirm忙灼,失敗了會(huì)執(zhí)行 Cancel匠襟,那 Confirm 階段失敗了怎么辦钝侠?這時(shí)候只能設(shè)置重試機(jī)制,不斷重試調(diào)失敗的 Confirm酸舍,直到成功為止帅韧,真有怎么也不成功的,就只能人工介入了啃勉。
TCC 需要根據(jù)每個(gè)場(chǎng)景和業(yè)務(wù)邏輯來(lái)設(shè)計(jì)相應(yīng)的操作忽舟,所以很大程度增加了業(yè)務(wù)代碼的復(fù)雜度,對(duì)業(yè)務(wù)有很大的侵入淮阐。
雖說(shuō)對(duì)業(yè)務(wù)有侵入叮阅,但是 TCC 沒(méi)有資源的阻塞,每一個(gè)方法都是直接提交事務(wù)的泣特,如果出錯(cuò)是通過(guò)業(yè)務(wù)層面的 Cancel 來(lái)進(jìn)行補(bǔ)償浩姥,所以也稱(chēng)補(bǔ)償性事務(wù)方法。
TCC 要注意的幾個(gè)問(wèn)題:
冪等問(wèn)題:因?yàn)榫W(wǎng)絡(luò)調(diào)用無(wú)法保證請(qǐng)求一定能到達(dá)群扶,所以都會(huì)有重調(diào)機(jī)制及刻,因此對(duì)于 Try、Confirm竞阐、Cancel 三個(gè)方法都需要冪等實(shí)現(xiàn)缴饭,避免重復(fù)執(zhí)行產(chǎn)生錯(cuò)誤。
空回滾問(wèn)題:指的是 Try 方法由于網(wǎng)絡(luò)問(wèn)題沒(méi)收到超時(shí)了骆莹,此時(shí)事務(wù)管理器就會(huì)發(fā)出 Cancel 命令颗搂,那么需要支持 Cancel 在未執(zhí)行 Try 的情況下能正常的 Cancel。
懸掛問(wèn)題:這個(gè)問(wèn)題也是指 Try 方法由于網(wǎng)絡(luò)阻塞超時(shí)觸發(fā)了事務(wù)管理器發(fā)出了 Cancel 命令幕垦,但是執(zhí)行了 Cancel 命令之后 Try 請(qǐng)求到了丢氢。所以空回滾之后還得記錄一下,防止 Try 的再調(diào)用先改。
4疚察、本地消息表
本地消息表分布式事務(wù)解決方案是國(guó)外的 eBay 提出的一套方案。其實(shí)就是利用了各系統(tǒng)本地的事務(wù)來(lái)實(shí)現(xiàn)分布式事務(wù)仇奶,在數(shù)據(jù)庫(kù)中存放一張事務(wù)消息表貌嫡,在執(zhí)行業(yè)務(wù)操作的時(shí)候, 將業(yè)務(wù)的執(zhí)行和將消息放入消息表中的操作放在同一個(gè)事務(wù)中该溯。
本地事務(wù)執(zhí)行成功之后再調(diào)用其他服務(wù)岛抄,如果成功了就將消息表里的消息狀態(tài)改為成功,如果失敗了狈茉,則由定時(shí)任務(wù)去讀取本地事務(wù)表中未成功的消息夫椭,再去調(diào)用相應(yīng)的服務(wù),成功后再次修改狀態(tài)氯庆。
這里也要設(shè)置重試機(jī)制蹭秋,一旦有實(shí)在不成功的扰付,還需人工介入。這里要注意的是感凤,也要保證對(duì)應(yīng)服務(wù)的方法冪等性悯周。
可以看出,本地消息表實(shí)現(xiàn)比較簡(jiǎn)單陪竿,是一種最大努力通知思想禽翼,實(shí)現(xiàn)的是最終一致性,容忍了數(shù)據(jù)暫時(shí)不一致的情況族跛。
缺點(diǎn)是嚴(yán)重依賴(lài)數(shù)據(jù)庫(kù)闰挡。
5、可靠消息最終一致性方案
在上面的本地消息表方案中礁哄,生產(chǎn)者需要額外創(chuàng)建消息表长酗,還需要對(duì)本地消息表進(jìn)行輪詢(xún),業(yè)務(wù)負(fù)擔(dān)較重桐绒。阿里開(kāi)源的 RocketMQ 4.3 之后的版本正式支持事務(wù)消息夺脾,該事務(wù)消息本質(zhì)上是把本地消息表放到 RocketMQ 上,解決生產(chǎn)端的消息發(fā)送與本地事務(wù)執(zhí)行的原子性問(wèn)題茉继。
服務(wù) A咧叭,先給 Broker (消息中間件) 發(fā)送一個(gè) Half Message(半消息),其實(shí)這個(gè)半消息已發(fā)送到 Broker 端烁竭,但是此消息的狀態(tài)被標(biāo)記為"不能投遞"菲茬,消費(fèi)者還看不到,處于這種狀態(tài)下的消息稱(chēng)為半消息派撕。
發(fā)送完 半消息后婉弹,服務(wù)A 執(zhí)行業(yè)務(wù)操作(本地事務(wù)),再根據(jù)操作結(jié)果:如果成功终吼,則向 Broker 發(fā)送 一個(gè) Commit 命令镀赌,這時(shí)半消息就變成了可以被消費(fèi)者消息;如果失敗际跪,則發(fā)送一個(gè) RollBack 命令佩脊,該消息則會(huì)被刪除。
如果是 Commit 那么服務(wù) B 就能收到這條消息垫卤,然后再做對(duì)應(yīng)的操作,做完了之后再消費(fèi)這條消息即可出牧。
如果 RocketMQ 沒(méi)有收到服務(wù) A 確認(rèn)狀態(tài)的消息穴肘,那么半消息 RocketMQ 會(huì)自動(dòng)定時(shí)輪詢(xún)回調(diào)你的接口,詢(xún)問(wèn)這個(gè)處理的處理情況舔痕。借助這點(diǎn)评抚,服務(wù)A實(shí)現(xiàn)一個(gè)回調(diào)豹缀,根據(jù)實(shí)際處理結(jié)果 Commit 或者 Rollback,加強(qiáng)一致性判斷慨代。
在服務(wù) B 執(zhí)行的過(guò)程中也可能會(huì)失敗邢笙,這時(shí)也是需要重試,一直執(zhí)行不成功也需要人工介入侍匙,同時(shí)也需要保證服務(wù) B 方法的冪等性氮惯。
6、最大努力通知
最大努力通知型( Best-effort delivery)是最簡(jiǎn)單的一種柔性事務(wù)想暗,適用于一些最終一致性時(shí)間敏感度低的業(yè)務(wù)妇汗,且被動(dòng)方處理結(jié)果不影響主動(dòng)方的處理結(jié)果。典型的使用場(chǎng)景:如銀行通知说莫、商戶(hù)通知等杨箭。
就本地消息表來(lái)說(shuō)會(huì)有后臺(tái)任務(wù)定時(shí)去查看未完成的消息,然后去調(diào)用對(duì)應(yīng)的服務(wù)储狭,當(dāng)一個(gè)消息多次調(diào)用都失敗的時(shí)候可以記錄下然后引入人工互婿,或者直接舍棄。這其實(shí)算是最大努力了
事務(wù)消息也是一樣辽狈,當(dāng)半消息被commit了之后確實(shí)就是普通消息了慈参,如果訂閱者一直不消費(fèi)或者消費(fèi)不了則會(huì)一直重試,到最后進(jìn)入死信隊(duì)列稻艰。其實(shí)這也算最大努力懂牧。
最大努力通知,發(fā)起通知方盡最大的努力將業(yè)務(wù)處理結(jié)果通知為接收通知方尊勿,但是可能消息接收不到僧凤,此時(shí)需要接收通知方主動(dòng)調(diào)用發(fā)起通知方的接口查詢(xún)業(yè)務(wù)處理結(jié)果,通知的可靠性關(guān)鍵在接收通知方元扔。
總結(jié)
其實(shí)分布式事務(wù)解決方案還有很多躯保,但是各自還是會(huì)存在很多問(wèn)題,極端情況下也都需要人工去處理澎语,而且大大提高了流程的復(fù)雜度途事,會(huì)帶來(lái)很多額外的開(kāi)銷(xiāo)。
所以謹(jǐn)記擅羞,在真實(shí)的開(kāi)發(fā)過(guò)程中尸变,能不使用分布式事務(wù)就不要使用!
后面會(huì)給大家?guī)?lái)分布式事務(wù)的實(shí)戰(zhàn)减俏,沒(méi)點(diǎn)關(guān)注的可以點(diǎn)個(gè)關(guān)注召烂,防止走丟了。
END
往期推薦
就這娃承?分布式 ID 發(fā)號(hào)器實(shí)戰(zhàn)
就這奏夫?Spring 事務(wù)失效場(chǎng)景及解決方案
SpringBoot+Redis 實(shí)現(xiàn)消息訂閱發(fā)布
更多精彩推薦,請(qǐng)關(guān)注公眾號(hào)【靚仔聊編程】