4月初朋友在面試一家互聯(lián)網(wǎng)公司的過程中就被問到了分布式事務(wù)問題肤无。說他又一次在沒有好好整理的問題上吃了虧仗岸,所以給大家分享一下谦纱。
背景
四月初蚓胸,去面試了本市的一家之前在做辦公室無人貨架的公司讶踪,雖然他們現(xiàn)在在面臨著轉(zhuǎn)型,但是對(duì)于我這種想從傳統(tǒng)企業(yè)往互聯(lián)網(wǎng)行業(yè)走的孩子來說,還是比較有吸引力的涨共。
在面試過程中就提到了分布式事務(wù)問題魁索。我又一次在沒有好好整理的問題上吃了虧当辐,記錄一下,還是長(zhǎng)記性 B礁场!囱井!
先看面試過程
面試官先是在紙上先畫了這樣一張圖:
讓我看這張圖按照上面的流程走,有沒有什么問題恕稠?面試官并沒有直接說出來這里面會(huì)有分布式事務(wù)的問題琅绅,而是讓我來告訴他,這就是面試套路呀鹅巍。
我回答了這中間可能存在分布式事務(wù)的問題千扶,當(dāng)步驟 2 在調(diào)用 B 系統(tǒng)時(shí),可能存在B 系統(tǒng)處理完成后骆捧,在響應(yīng)的時(shí)候超時(shí)澎羞,導(dǎo)致 A 系統(tǒng)誤認(rèn)為 B 處理失敗了,從而導(dǎo)致A 系統(tǒng)回滾敛苇,跟 B 系統(tǒng)存在數(shù)據(jù)不一致的情況妆绞。
ok ,我回答到這里枫攀,應(yīng)該回答了面試官的第一層意思括饶,至少我有這種意識(shí),他點(diǎn)了點(diǎn)頭来涨。
接著图焰,他繼續(xù)問:“那你有什么好的解決方式嗎?”
此時(shí)我腦子里面只有兩階段提交的大概流程圖的印象蹦掐,然后巴卡巴拉的跟他說了一番技羔,什么中間來個(gè)協(xié)調(diào)者呀僵闯,先預(yù)提交什么的,如果有失敗藤滥,就 rollback鳖粟,如果 ok,再真正的提交事務(wù)拙绊,就是網(wǎng)上這些大神們說的這些理論向图。
然后面試官就繼續(xù)問:那A 在調(diào)用 B 的這條線斷了,你們代碼具體是怎么處理的呢 标沪?怎么來做到 rollback 的呢 张漂?說說你代碼怎么寫的。
此時(shí)谨娜,我懵了。
最后結(jié)果磺陡,大家肯定也能猜到趴梢,涼涼。
什么是事務(wù)
這里我們說的事務(wù)一般是指數(shù)據(jù)庫(kù)事務(wù)币他,簡(jiǎn)稱事務(wù)坞靶,是數(shù)據(jù)庫(kù)管理系統(tǒng)執(zhí)行過程中的一個(gè)邏輯單位,由一個(gè)有限的數(shù)據(jù)庫(kù)操作序列構(gòu)成蝴悉。維基百科中這么說的彰阴。
用轉(zhuǎn)賬的例子來說,A 賬戶要給 B 賬戶轉(zhuǎn) 100塊拍冠,這中間至少包含了兩個(gè)操作:
- A 賬戶 減 100 塊
- B 賬戶 加 100 塊
在支持事務(wù)的數(shù)據(jù)庫(kù)管理系統(tǒng)來說尿这,就是得確保上面兩個(gè)操作(整個(gè)“事務(wù)”)都能完成,不能存在庆杜,A 的100塊扣了射众,然后B 的賬戶又沒加上去的情況。
數(shù)據(jù)庫(kù)事務(wù)包含了四個(gè)特性晃财,分別是:
- 原子性(Atomicity):事務(wù)作為一個(gè)整體被執(zhí)行叨橱,包含在其中的對(duì)數(shù)據(jù)庫(kù)的操作要么全部被執(zhí)行,要么都不執(zhí)行断盛。對(duì)于轉(zhuǎn)賬來說罗洗,A賬戶扣錢,B 賬戶加錢钢猛,要么同時(shí)成功伙菜,要么同時(shí)失敗。
- 一致性(Consistency):事務(wù)應(yīng)確保數(shù)據(jù)庫(kù)的狀態(tài)從一個(gè)一致狀態(tài)轉(zhuǎn)變?yōu)榱硪粋€(gè)一致狀態(tài)厢洞。一致狀態(tài)的含義是數(shù)據(jù)庫(kù)中的數(shù)據(jù)應(yīng)滿足完整性約束
- 隔離性(Isolation):多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)仇让,一個(gè)事務(wù)的執(zhí)行不應(yīng)影響其他事務(wù)的執(zhí)行典奉。其他賬戶在轉(zhuǎn)賬的時(shí)候,不能影響到上面的 A 跟 B 之前的交易丧叽。
- 持久性(Durability):已被提交的事務(wù)對(duì)數(shù)據(jù)庫(kù)的修改應(yīng)該永久保存在數(shù)據(jù)庫(kù)中卫玖。
什么是分布式事務(wù)
我們知道,上面的轉(zhuǎn)賬 我們是在一個(gè)數(shù)據(jù)庫(kù)中的事務(wù)操作踊淳。我們可以使用一些框架 比如 Spring 的事務(wù)管理器來給我們統(tǒng)一搞定假瞬。
但是如果我們系統(tǒng)中出現(xiàn)垮庫(kù)操作,比如一個(gè)操作中迂尝,我需要操作多個(gè)庫(kù)脱茉,或者說這個(gè)操作會(huì)垮應(yīng)用之前的調(diào)用,那么Spring 的事務(wù)管理機(jī)制就對(duì)這種場(chǎng)景沒有辦法了垄开。
就像上面面試題中出現(xiàn)的問題一樣琴许,在系統(tǒng) A 的步驟 2 在遠(yuǎn)程調(diào)用 B 的時(shí)候,由于網(wǎng)絡(luò)超時(shí)溉躲,導(dǎo)致B 沒有正常響應(yīng)榜田,但是A 這邊調(diào)用失敗,進(jìn)行了回滾锻梳,而 B 又提交了事務(wù)箭券。此時(shí)就可能會(huì)導(dǎo)致數(shù)據(jù)不一致的情況,參生分布式事務(wù)的問題疑枯。
分布式事務(wù)的存在辩块,就是解決數(shù)據(jù)不一致的情況。
為什么我們要保證一致性
CAP 理論
分布式系統(tǒng)中有這么一個(gè)廣為流傳的理論:CAP 定理
這個(gè)定理呀荆永,起源于加州大學(xué)柏克萊分校(University of California, Berkeley)的計(jì)算機(jī)科學(xué)家埃里克·布魯爾在 2000年的分布式計(jì)算原理研討會(huì)(PODC)上提出的一個(gè)猜想废亭。后來在2002年,麻省理工學(xué)院(MIT)的賽斯·吉爾伯特和南暇咴浚·林奇發(fā)表了布魯爾猜想的證明滔以,使之成為一個(gè)定理∶テ矗【摘自維基百科】
他說呀你画,對(duì)于一個(gè)分布式計(jì)算系統(tǒng)來說,不可能同時(shí)滿足以下三點(diǎn):
- 一致性(Consistency)
- 可用性(Availability)
- 分區(qū)容錯(cuò)性(Partition tolerance)
而一個(gè)分布式系統(tǒng)最多只能滿足其中的兩項(xiàng)桃漾。
那么坏匪,上面的三點(diǎn)分別是什么玩意兒?為什么又只能同時(shí)滿足兩項(xiàng)呢撬统?
我們先看這樣一個(gè)場(chǎng)景适滓,現(xiàn)在我們系統(tǒng)部署了兩份(兩個(gè)節(jié)點(diǎn),web1 和 web2 ),同樣的業(yè)務(wù)代碼恋追,但是維護(hù)的是自己這個(gè)節(jié)點(diǎn)生成的數(shù)據(jù)凭迹。但是用戶訪問進(jìn)來罚屋,可能會(huì)訪問到不同的節(jié)點(diǎn)。但是不管是訪問web1 還是web2 ,在用戶參數(shù)數(shù)據(jù) 過后嗅绸,這個(gè)數(shù)據(jù)都必須得同步到另外的節(jié)點(diǎn)脾猛,讓用戶不管訪問哪個(gè)節(jié)點(diǎn),都是響應(yīng)他需要的數(shù)據(jù)鱼鸠。如下圖:
分區(qū)容錯(cuò)性
我們先說分區(qū)容錯(cuò)性:也就是說呀猛拴,就算上面這兩個(gè)節(jié)點(diǎn)之間發(fā)生了網(wǎng)絡(luò)故障,無法發(fā)生同步的問題蚀狰,但是用戶訪問進(jìn)來愉昆,不管到哪個(gè)節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)都得單獨(dú)提供服務(wù)麻蹋,這一點(diǎn)對(duì)于互聯(lián)網(wǎng)公司來說跛溉,是必須要滿足的。
當(dāng) web1 和 web2 之間的網(wǎng)絡(luò)發(fā)生故障扮授,導(dǎo)致數(shù)據(jù)無法進(jìn)行同步倒谷。用戶在web1 上寫了數(shù)據(jù),馬上又訪問進(jìn)來讀取數(shù)據(jù)糙箍,請(qǐng)求到了 web2,但是此時(shí) web2 是沒有數(shù)據(jù)的牵祟。那么我們是 給用戶返回 null 深夯?還是說給一些提示,說系統(tǒng)不可用诺苹,稍后重試呢咕晋?
都不妥吧,兄弟收奔。
一致性
如果要保證可用性掌呜,那么有數(shù)據(jù)的節(jié)點(diǎn)返回?cái)?shù)據(jù),沒數(shù)據(jù)的節(jié)點(diǎn)返回 null ,就會(huì)出現(xiàn)用戶那里看到的是一會(huì)兒有數(shù)據(jù)坪哄,一會(huì)兒沒有數(shù)據(jù)质蕉,此時(shí)就存在一致性的問題。
可用性
如果保證一致性翩肌,那么在用戶訪問的時(shí)候模暗,不管 web1 還是web2 ,我們可能會(huì)返回一些提示信息念祭,說系統(tǒng)不可用兑宇,稍后再試等等,保證每次都是一致的粱坤。明明我們有數(shù)據(jù)在隶糕,但是我們系統(tǒng)卻響應(yīng)的是提示信息瓷产,此時(shí)就是可用性的問題。
由于分區(qū)容錯(cuò)性(P)是必須保證的枚驻,那么我們分布式系統(tǒng)就更多是在一致性(CP) 和可用性(AP)上做平衡了濒旦,只能同時(shí)滿足兩個(gè)條件。
其實(shí)测秸,大家想想疤估,ZK 是不是就是嚴(yán)格實(shí)現(xiàn)了 CP ,而 Eureka 則是保證了 AP霎冯。
其實(shí)分布式事務(wù)強(qiáng)調(diào)的就是一致性铃拇。
幾種分布式事務(wù)解決方案
2PC
在說 2PC 之前,我們先了解一下XA規(guī)范是個(gè)什么東西沈撞?
XA規(guī)范描述了全局的事務(wù)管理器與局部的資源管理器之間的接口慷荔。XA規(guī)范的目的是允許多個(gè)資源(如數(shù)據(jù)庫(kù),應(yīng)用服務(wù)器缠俺,消息隊(duì)列显晶,等等)在同一事務(wù)中訪問,這樣可以使ACID屬性跨越應(yīng)用程序而保持有效壹士。
XA使用兩階段提交(2PC)來保證所有資源同時(shí)提交或回滾任何特定的事務(wù)磷雇。
大家想一個(gè)場(chǎng)景,在做單應(yīng)用的時(shí)候躏救,有的同學(xué)連過兩個(gè)庫(kù)吧唯笙?在一個(gè)事務(wù)中會(huì)同時(shí)向兩個(gè)系統(tǒng)插入數(shù)據(jù)。但是對(duì)于普通事務(wù)來講盒使,是管不了的崩掘。
看下圖(只是舉例這種操作的套路,不局限于下面的業(yè)務(wù)):
一個(gè)服務(wù)里面要去操作兩個(gè)庫(kù)少办,如何保證事務(wù)成功呢苞慢。
這里我們介紹一個(gè)框架 Atomikos ,他就是實(shí)現(xiàn)了這種 XA 的套路英妓⊥旆牛看代碼:
看到上面的圖了哇,Atomikos 自己實(shí)現(xiàn)了一個(gè)事務(wù)管理器蔓纠。我們獲取的連接都從它哪里拿骂维。
- 第一步先開啟事務(wù),然后進(jìn)行預(yù)提交贺纲,db1 和 db2 都先進(jìn)行預(yù)先執(zhí)行航闺,注意:這里沒有提交事務(wù)。
- 第二步才是真正的提交事務(wù),由 Atomikos 來發(fā)起提交的潦刃,如果出現(xiàn)異常則發(fā)起回滾操作侮措。
這個(gè)過程是不是就有兩個(gè)角色了,一個(gè) 事務(wù)管理器乖杠,一個(gè)資源管理器(我們這里是 數(shù)據(jù)庫(kù)分扎,也可以是其他的組件,消息隊(duì)列什么的)胧洒。
整個(gè)執(zhí)行過程是這樣:
上圖是正常情況畏吓,下圖是一方出現(xiàn)故障的情況。
第一階段(提交請(qǐng)求階段):
- 協(xié)調(diào)者節(jié)點(diǎn)向所有參與者節(jié)點(diǎn)詢問是否可以執(zhí)行提交操作卫漫,并開始等待各參與者節(jié)點(diǎn)的響應(yīng)菲饼。
- 參與者節(jié)點(diǎn)執(zhí)行詢問發(fā)起為止的所有事務(wù)操作,并將Undo信息和Redo信息寫入日志列赎。
- 各參與者節(jié)點(diǎn)響應(yīng)協(xié)調(diào)者節(jié)點(diǎn)發(fā)起的詢問宏悦。如果參與者節(jié)點(diǎn)的事務(wù)操作實(shí)際執(zhí)行成功,則它返回一個(gè)”同意”消息包吝;如果參與者節(jié)點(diǎn)的事務(wù)操作實(shí)際執(zhí)行失敗饼煞,則它返回一個(gè)”中止”消息。
第二階段 (提交執(zhí)行階段):
成功诗越,當(dāng)協(xié)調(diào)者節(jié)點(diǎn)從所有參與者節(jié)點(diǎn)獲得的相應(yīng)消息都為”同意”時(shí):
- 協(xié)調(diào)者節(jié)點(diǎn)向所有參與者節(jié)點(diǎn)發(fā)出”正式提交”的請(qǐng)求砖瞧。
- 參與者節(jié)點(diǎn)正式完成操作,并釋放在整個(gè)事務(wù)期間內(nèi)占用的資源嚷狞。
- 參與者節(jié)點(diǎn)向協(xié)調(diào)者節(jié)點(diǎn)發(fā)送”完成”消息块促。
- 協(xié)調(diào)者節(jié)點(diǎn)收到所有參與者節(jié)點(diǎn)反饋的”完成”消息后,完成事務(wù)感耙。
失敗,如果任一參與者節(jié)點(diǎn)在第一階段返回的響應(yīng)消息為”終止”持隧,或者 協(xié)調(diào)者節(jié)點(diǎn)在第一階段的詢問超時(shí)之前無法獲取所有參與者節(jié)點(diǎn)的響應(yīng)消息時(shí):
- 協(xié)調(diào)者節(jié)點(diǎn)向所有參與者節(jié)點(diǎn)發(fā)出”回滾操作”的請(qǐng)求即硼。
- 參與者節(jié)點(diǎn)利用之前寫入的Undo信息執(zhí)行回滾,并釋放在整個(gè)事務(wù)期間內(nèi)占用的資源屡拨。
- 參與者節(jié)點(diǎn)向協(xié)調(diào)者節(jié)點(diǎn)發(fā)送”回滾完成”消息只酥。
- 協(xié)調(diào)者節(jié)點(diǎn)收到所有參與者節(jié)點(diǎn)反饋的”回滾完成”消息后,取消事務(wù)呀狼。
有時(shí)候裂允,第二階段也被稱作完成階段,因?yàn)闊o論結(jié)果怎樣哥艇,協(xié)調(diào)者都必須在此階段結(jié)束當(dāng)前事務(wù)绝编。
可靠消息最終一致性方案
基于普通的消息隊(duì)列中間件
上面我們說了兩階段提交的方案,接下來我們講講怎么基于可靠消息最終一致性方案來解決分布式事務(wù)的問題。
這個(gè)方案十饥,就有消息服務(wù)中間件角色參與進(jìn)來了窟勃。我們先看一個(gè)大提的流程圖:
我們以創(chuàng)建訂單下單過程和 后面出庫(kù) 的流程為例來講述上面的圖。
在下單邏輯里面(Producer 端)逗堵,我們先生成一個(gè)訂單的數(shù)據(jù)秉氧,比如訂單號(hào),數(shù)量等關(guān)鍵的信息蜒秤,先包裝成一條消息汁咏,并把消息的狀態(tài)置為 init ,然后發(fā)送到 獨(dú)立消息服務(wù)中,并且入庫(kù)作媚。
接下來繼續(xù)處理 下單的其他本地的邏輯攘滩。
處理完成后,走到確認(rèn)發(fā)送消息這一步掂骏,說明我的訂單是能夠下成功的轰驳。那么我們?cè)傧蛳⒎?wù)里面發(fā)送一條confirm 的消息,消息服務(wù)里面就可以把這個(gè)訂單的消息狀態(tài)修改為 send 并且弟灼,發(fā)送到消息隊(duì)列里面级解。
接下來,消費(fèi)者端去消費(fèi)這條消息田绑。處理自己這邊的邏輯勤哗,處理完成以后,然后反饋消息處理結(jié)果到獨(dú)立消息服務(wù)掩驱,獨(dú)立消息服務(wù)把消息狀態(tài)置為 end 狀態(tài) ,表示結(jié)束芒划。但是得注意保證接口的冪等性,避免重復(fù)消費(fèi)帶來的問題欧穴。
這里面可能出現(xiàn)的問題民逼,以及各個(gè)步驟怎么解決的:
- 比如在 prepare 階段就發(fā)生異常,那么這里訂單這塊都不會(huì)下成功涮帘。但是我們說拼苍,我們這里是基于可靠消息,得保證我們的消息服務(wù)是正常的调缨。
- 在 comfirm 出現(xiàn)異常疮鲫,此時(shí)發(fā)送確認(rèn)失敗,但是我們的單已經(jīng)下成功了弦叶。這種情況俊犯,我們就可以在獨(dú)立消息服務(wù)中起一個(gè)定時(shí)任務(wù),定時(shí)去查詢 消息狀態(tài)為 init 的數(shù)據(jù)伤哺,去反向查詢 訂單系統(tǒng)中的單號(hào)是否存在燕侠,如果存在者祖,那么我們就把消息置為 send 狀態(tài),然后發(fā)送到 消息隊(duì)列里面贬循,如果查詢到不存在的訂單咸包,那么就直接拋棄掉這條消息。所以這里我們的訂單系統(tǒng)得提供批量查詢訂單的接口杖虾,還有下游的消費(fèi)系統(tǒng)得保證冪等烂瘫。保證重復(fù)消費(fèi)的一致性。
- 消息隊(duì)列丟消息或者下游系統(tǒng)一直處理失敗奇适,導(dǎo)致沒有消息反饋過來坟比,出現(xiàn)一直是 send 狀態(tài)的消息。此時(shí)獨(dú)立消息我們還需要一個(gè)定時(shí)任務(wù)嚷往,就是處理這種 send 狀態(tài)的消息葛账,我們可以進(jìn)行重發(fā),直到后面系統(tǒng)消費(fèi)成功為止皮仁。
- 最后消費(fèi)者這端籍琳,我們?cè)谙M(fèi)的時(shí)候,如果出現(xiàn)消費(fèi)異常贷祈,或者是系統(tǒng)bug 導(dǎo)致異常的情況趋急。那么這里我們還可以去記錄日志,如果不是系統(tǒng)代碼問題势誊,是網(wǎng)絡(luò)抖動(dòng)導(dǎo)致的呜达,那么在上面第三種情況,消息系統(tǒng)會(huì)重新發(fā)送消息粟耻,我們?cè)偬幚砭褪遣榻H绻且恢笔。憔鸵紤]是不是你的代碼真的有問題挤忙,有bug 了吧霜威。
- 最后的保底方案,記錄日志册烈,出現(xiàn)問題人肉處理數(shù)據(jù)「昶茫現(xiàn)在我們系統(tǒng)出現(xiàn)錯(cuò)誤,以目前的技術(shù)手段是沒辦法做到都靠機(jī)器去解決的茄厘,都得靠我們?nèi)税?jù)我了解谈宛,現(xiàn)在很多大廠都會(huì)有這樣的人次哈,專門處理這種類型的問題,手動(dòng)去修改數(shù)據(jù)庫(kù)的方式吆录。我們之前待的小廠窑滞,基本上都是靠我們自己去寫 sql 去修改數(shù)據(jù)的,想想,是不是哀卫?
貼一下關(guān)鍵的獨(dú)立消息服務(wù)核心邏輯代碼框架:
定時(shí)任務(wù):
基于 RocketMQ實(shí)現(xiàn)
這種方案巨坊,跟上面的獨(dú)立消息服務(wù)一致,這里直接去掉獨(dú)立服務(wù)此改,只利用消息隊(duì)列來實(shí)現(xiàn)趾撵,也就是阿里的 RocketMQ 。
流程圖如下:
針對(duì)這里的可靠消息最終一致性方案來說共啃,我們說的可靠是指保證消息一定能發(fā)送到消息中間件里面去占调,保證這里可靠。
對(duì)于下游的系統(tǒng)來說移剪,消費(fèi)不成功究珊,一般來說就是采取失敗重試,重試多次不成功纵苛,那么就記錄日志剿涮,后續(xù)人工介入來進(jìn)行處理。所以這里得強(qiáng)調(diào)一下攻人,后面的系統(tǒng)取试,一定要處理冪等,重試贝椿,日志這幾個(gè)東西想括。
如果是對(duì)于資金類的業(yè)務(wù),后續(xù)系統(tǒng)回滾了以后烙博,得想辦法去通知前面的系統(tǒng)也進(jìn)行回滾瑟蜈,或者是發(fā)送報(bào)警由人工來手工回滾和補(bǔ)償。
TCC 方案
TCC 的全程分為三個(gè)階段渣窜,分別是 Try铺根、Confirm、Cancel:
- Try階段:這個(gè)階段說的是對(duì)各個(gè)服務(wù)的資源做檢測(cè)以及對(duì)資源進(jìn)行鎖定或者預(yù)留
- Confirm階段:這個(gè)階段說的是在各個(gè)服務(wù)中執(zhí)行實(shí)際的操作
- Cancel階段:如果任何一個(gè)服務(wù)的業(yè)務(wù)方法執(zhí)行出錯(cuò)乔宿,那么這里就需要進(jìn)行補(bǔ)償位迂,就是執(zhí)行已經(jīng)執(zhí)行成功的業(yè)務(wù)邏輯的回滾操作
還是以轉(zhuǎn)賬的例子為例,在跨銀行進(jìn)行轉(zhuǎn)賬的時(shí)候详瑞,需要涉及到兩個(gè)銀行的分布式事務(wù)掂林,從A 銀行向 B 銀行轉(zhuǎn) 1 塊,如果用TCC 方案來實(shí)現(xiàn):
大概思路就是這樣的:
- Try 階段:先把A 銀行賬戶先凍結(jié) 1 塊坝橡,B銀行賬戶中的資金給預(yù)加 1 塊泻帮。
- Confirm 階段:執(zhí)行實(shí)際的轉(zhuǎn)賬操作,A銀行賬戶的資金扣減 1塊计寇,B 銀行賬戶的資金增加 1 塊锣杂。
- Cancel 階段:如果任何一個(gè)銀行的操作執(zhí)行失敗脂倦,那么就需要回滾進(jìn)行補(bǔ)償,就是比如A銀行賬戶如果已經(jīng)扣減了元莫,但是B銀行賬戶資金增加失敗了赖阻,那么就得把A銀行賬戶資金給加回去。
這種方案就比較復(fù)雜了踱蠢,一步操作要做多個(gè)接口來配合完成火欧。
最開始 A 銀行賬戶 與 B 銀行賬戶都分別為:amount(數(shù)量)=1000,frozen(凍結(jié)金額)= 0
從A銀行賬戶發(fā)起轉(zhuǎn)賬到 B 銀行賬戶 1 塊:
try 階段:A 銀行賬戶金額減 1茎截,凍結(jié)金額 加 1布隔,B 銀行 賬戶 凍結(jié)金額加 1 。
此時(shí):
- A 銀行賬戶:amount(數(shù)量)= 1000 - 1 = 999稼虎,frozen(凍結(jié)金額)= 0 + 1 = 1
- B 銀行賬戶:amount(數(shù)量)= 1000衅檀,frozen(凍結(jié)金額)= 0 + 1 = 1
confirm 階段 : A銀行賬戶凍結(jié)金額 減 1,B 銀行賬戶金額 加 1霎俩,凍結(jié)金額 減 1
此時(shí):
- A 銀行賬戶:amount(數(shù)量)= 999哀军,frozen(凍結(jié)金額)= 1 - 1 = 0
- B 銀行賬戶:amount(數(shù)量)= 1000 + 1 = 1001,frozen(凍結(jié)金額)= 1 - 1 = 0
cancel 階段: A 銀行賬戶金額 + 1打却,凍結(jié)金額 -1 杉适,B 銀行 凍結(jié)金額 -1
此時(shí):
- A 銀行賬戶:amount(數(shù)量)= 999 + 1 = 1000,frozen(凍結(jié)金額)= 1 - 1 = 0
- B 銀行賬戶:amount(數(shù)量)= 1000柳击,frozen(凍結(jié)金額)= 1 - 1 = 0
至此猿推,整個(gè)過程就演示完畢,大家記得跑一遍代碼捌肴。其實(shí)還是蠻復(fù)雜的蹬叭,有許多接口一起來配合完成整個(gè)業(yè)務(wù),試想一下状知,如果我們項(xiàng)目中大量用到 TCC 來寫秽五,你受得了?
再提一下 BASE理論
BASE 理論是 Basically Available(基本可用)饥悴,Soft State(軟狀態(tài))和Eventually Consistent(最終一致性)三個(gè)短語的縮寫坦喘。
- 基本可用(Basically Available): 指分布式系統(tǒng)在出現(xiàn)不可預(yù)知故障的時(shí)候,允許損失部分可用性西设。
- 軟狀態(tài)( Soft State):指允許系統(tǒng)中的數(shù)據(jù)存在中間狀態(tài)瓣铣,并認(rèn)為該中間狀態(tài)的存在不會(huì)影響系統(tǒng)的整體可用性,即允許系統(tǒng)在不同節(jié)點(diǎn)的數(shù)據(jù)副本之間進(jìn)行數(shù)據(jù)同步的過程存在延時(shí)贷揽。
- 最終一致( Eventual Consistency):強(qiáng)調(diào)的是所有的數(shù)據(jù)更新操作棠笑,在經(jīng)過一段時(shí)間的同步之后,最終都能夠達(dá)到一個(gè)一致的狀態(tài)擒滑。因此腐晾,最終一致性的本質(zhì)是需要系統(tǒng)保證最終數(shù)據(jù)能夠達(dá)到一致,而不需要實(shí)時(shí)保證系統(tǒng)數(shù)據(jù)的強(qiáng)一致性丐一。
其核心思想是:
即使無法做到強(qiáng)一致性(Strong consistency)藻糖,但每個(gè)應(yīng)用都可以根據(jù)自身的業(yè)務(wù)特點(diǎn),采用適當(dāng)?shù)姆绞絹硎瓜到y(tǒng)達(dá)到最終一致性(Eventual consistency)
到這里大家再想想库车, 上面 TCC 方案中的賬戶設(shè)計(jì)了一個(gè)凍結(jié)字段frozen巨柒,這里是不是就是BASE理論中間的軟狀態(tài)呢 ?
最后
對(duì)存在非常多的微服務(wù)的公司來說柠衍,服務(wù)之間的調(diào)用異常的復(fù)雜洋满,那么在引入分布式事務(wù)的過程中,你需要考慮加入分布式事務(wù)后珍坊,系統(tǒng)實(shí)現(xiàn)起來的復(fù)雜性和開發(fā)成本牺勾,或者說哪些地方根本就不需要搞分布式事務(wù)。
其實(shí)沒必要到處都搞分布式事務(wù)阵漏,對(duì)于大多數(shù)的業(yè)務(wù)來說驻民,其實(shí)我們并不需要做分布式事務(wù),直接做日志履怯,做監(jiān)控就好了回还。然后出現(xiàn)問題,手工去處理叹洲,一個(gè)月也不會(huì)有那么多的問題的柠硕。如果你天天都出現(xiàn)這些問題,你是不是要好好去排查排查你的代碼Bug了运提。
對(duì)于資金類的場(chǎng)景蝗柔,那么基本上會(huì)采用分布式事務(wù)方案來保證,像其他的服務(wù)民泵,會(huì)員诫咱,積分,商品信息呀這些洪灯,可能就不需要這么去搞了坎缭。