什么是分布式事務(wù)
分布式事務(wù)是指事務(wù)的參與者、支持事務(wù)的服務(wù)器、資源服務(wù)器以及事務(wù)管理器「分別位于不同的分布式系統(tǒng)的不同節(jié)點(diǎn)之上」奴潘。
一個大的操作由N多的小的操作共同完成。而這些小的操作又分布在不同的服務(wù)上雀扶。針對于這些操作杖小,「要么全部成功執(zhí)行,要么全部不執(zhí)行」愚墓。
為什么會有分布式事務(wù)予权?
舉個例子:
轉(zhuǎn)賬是最經(jīng)典的分布式事務(wù)場景,假設(shè)用戶 A 使用銀行 app 發(fā)起一筆跨行轉(zhuǎn)賬給用戶 B浪册,銀行系統(tǒng)首先扣掉用戶 A 的錢扫腺,然后增加用戶 B 賬戶中的余額。
如果其中某個步驟失敗村象,此時就有可能會出現(xiàn) 2 種「異嘲驶罚」情況:
1.用戶 A 的賬戶扣款成功,用戶 B 賬戶余額增加失敗
2.用戶 A 賬戶扣款失敗厚者,用戶 B 賬戶余額增加成功躁劣。
對于銀行系統(tǒng)來說,以上 2 種情況都是「不允許發(fā)生」库菲,此時就需要事務(wù)來保證轉(zhuǎn)賬操作的成功账忘。
在「單體應(yīng)用」中,我們只需要貼上@Transactional注解就可以開啟事務(wù)來保證整個操作的「原子性」熙宇。
但是看似以上簡單的操作鳖擒,在實(shí)際的應(yīng)用架構(gòu)中,不可能是單體的服務(wù)烫止,我們會把這一系列操作交給「N個服務(wù)」去完成蒋荚,也就是拆分成為「分布式微服務(wù)架構(gòu)」。
比如下訂單服務(wù)馆蠕,扣庫存服務(wù)等等圆裕,必須要「保證不同服務(wù)狀態(tài)結(jié)果的一致性」,于是就出現(xiàn)了分布式事務(wù)荆几。
分布式理論
CAP定理
在一個分布式系統(tǒng)中,以下三點(diǎn)特性無法同時滿足赊时,「魚與熊掌不可兼得」
一致性(C):
在分布式系統(tǒng)中的所有數(shù)據(jù)備份吨铸,「在同一時刻是否擁有同樣的值」。(等同于所有節(jié)點(diǎn)訪問同一份最新的數(shù)據(jù)副本)
可用性(A):
在集群中一部分節(jié)點(diǎn)「故障」后祖秒,集群整體「是否還能響應(yīng)」客戶端的讀寫請求诞吱。(對數(shù)據(jù)更新具備高可用性)
分區(qū)容錯性(P):
即使出現(xiàn)「單個組件無法可用,操作依然可以完成」舟奠。
具體地講在分布式系統(tǒng)中,在任何數(shù)據(jù)庫設(shè)計(jì)中房维,一個Web應(yīng)用「至多只能同時支持上面的兩個屬性」沼瘫。顯然,任何橫向擴(kuò)展策略都要依賴于數(shù)據(jù)分區(qū)咙俩。因此耿戚,設(shè)計(jì)人員必須在一致性與可用性之間做出選擇。
BASE理論
在分布式系統(tǒng)中阿趁,我們往往追求的是可用性膜蛔,它的重要程序比一致性要高,那么如何實(shí)現(xiàn)高可用性呢脖阵?
前人已經(jīng)給我們提出來了另外一個理論皂股,就是BASE理論,它是用來對CAP定理進(jìn)行進(jìn)一步擴(kuò)充的呜呐。BASE理論指的是:
- 「Basically Available(基本可用)」
- 「Soft state(軟狀態(tài))」
- 「Eventually consistent(最終一致性)」
BASE理論是對CAP中的一致性和可用性進(jìn)行一個權(quán)衡的結(jié)果,理論的核心思想就是:我們無法做到強(qiáng)一致悍募,但每個應(yīng)用都可以根據(jù)自身的業(yè)務(wù)特點(diǎn)蘑辑,采用適當(dāng)?shù)姆绞絹硎瓜到y(tǒng)達(dá)到最終一致性(Eventual consistency)。
分布式事務(wù)解決方案
兩階段提交(2PC)
熟悉mysql的同學(xué)對兩階段提交應(yīng)該頗為熟悉搜立,mysql的事務(wù)就是通過「日志系統(tǒng)」來完成兩階段提交的以躯。
兩階段協(xié)議可以用于單機(jī)集中式系統(tǒng),由事務(wù)管理器協(xié)調(diào)多個資源管理器啄踊;也可以用于分布式系統(tǒng)忧设,「由一個全局的事務(wù)管理器協(xié)調(diào)各個子系統(tǒng)的局部事務(wù)管理器完成兩階段提交」。
這個協(xié)議有「兩個角色」颠通,
A節(jié)點(diǎn)是事務(wù)的協(xié)調(diào)者址晕,B和C是事務(wù)的參與者。
事務(wù)的提交分成兩個階段
第一個階段是「投票階段」
- 1.協(xié)調(diào)者首先將命令「寫入日志」
- 「發(fā)一個prepare命令」給B和C節(jié)點(diǎn)這兩個參與者
- 3.B和C收到消息后顿锰,根據(jù)自己的實(shí)際情況谨垃,「判斷自己的實(shí)際情況是否可以提交」
- 4.將處理結(jié)果「記錄到日志」系統(tǒng)
- 5.將結(jié)果「返回」給協(xié)調(diào)者
第二個階段是「決定階段」
當(dāng)A節(jié)點(diǎn)收到B和C參與者所有的確認(rèn)消息后
「判斷」所有協(xié)調(diào)者「是否都可以提交」
如果可以則「寫入日志」并且發(fā)起commit命令
有一個不可以則「寫入日志」并且發(fā)起abort命令
參與者收到協(xié)調(diào)者發(fā)起的命令,「執(zhí)行命令」
將執(zhí)行命令及結(jié)果「寫入日志」
「返回結(jié)果」給協(xié)調(diào)者
可能會存在哪些問題硼控?
「單點(diǎn)故障」:一旦事務(wù)管理器出現(xiàn)故障刘陶,整個系統(tǒng)不可用
「數(shù)據(jù)不一致」:在階段二,如果事務(wù)管理器只發(fā)送了部分 commit 消息牢撼,此時網(wǎng)絡(luò)發(fā)生異常匙隔,那么只有部分參與者接收到 commit 消息,也就是說只有部分參與者提交了事務(wù)熏版,使得系統(tǒng)數(shù)據(jù)不一致纷责。
「響應(yīng)時間較長」:整個消息鏈路是串行的捍掺,要等待響應(yīng)結(jié)果,不適合高并發(fā)的場景
「不確定性」:當(dāng)事務(wù)管理器發(fā)送 commit 之后再膳,并且此時只有一個參與者收到了 commit挺勿,那么當(dāng)該參與者與事務(wù)管理器同時宕機(jī)之后,重新選舉的事務(wù)管理器無法確定該條消息是否提交成功喂柒。
三階段提交(3PC)
三階段提交又稱3PC不瓶,相對于2PC來說增加了CanCommit階段和超時機(jī)制。如果段時間內(nèi)沒有收到協(xié)調(diào)者的commit請求胳喷,那么就會自動進(jìn)行commit湃番,解決了2PC單點(diǎn)故障的問題。
但是性能問題和不一致問題仍然沒有根本解決吭露。下面我們還是一起看下三階段流程的是什么樣的吠撮?
第一階段:「CanCommit階段」這個階段所做的事很簡單,就是協(xié)調(diào)者詢問事務(wù)參與者讲竿,你是否有能力完成此次事務(wù)泥兰。
如果都返回yes,則進(jìn)入第二階段
有一個返回no或等待響應(yīng)超時题禀,則中斷事務(wù)鞋诗,并向所有參與者發(fā)送abort請求
第二階段:「PreCommit階段」此時協(xié)調(diào)者會向所有的參與者發(fā)送PreCommit請求,參與者收到后開始執(zhí)行事務(wù)操作迈嘹,并將Undo和Redo信息記錄到事務(wù)日志中削彬。參與者執(zhí)行完事務(wù)操作后(此時屬于未提交事務(wù)的狀態(tài)),就會向協(xié)調(diào)者反饋“Ack”表示我已經(jīng)準(zhǔn)備好提交了秀仲,并等待協(xié)調(diào)者的下一步指令融痛。
第三階段:「DoCommit階段」在階段二中如果所有的參與者節(jié)點(diǎn)都可以進(jìn)行PreCommit提交,那么協(xié)調(diào)者就會從“預(yù)提交狀態(tài)”轉(zhuǎn)變?yōu)椤疤峤粻顟B(tài)”神僵。然后向所有的參與者節(jié)點(diǎn)發(fā)送"doCommit"請求雁刷,參與者節(jié)點(diǎn)在收到提交請求后就會各自執(zhí)行事務(wù)提交操作,并向協(xié)調(diào)者節(jié)點(diǎn)反饋“Ack”消息保礼,協(xié)調(diào)者收到所有參與者的Ack消息后完成事務(wù)沛励。相反,如果有一個參與者節(jié)點(diǎn)未完成PreCommit的反饋或者反饋超時炮障,那么協(xié)調(diào)者都會向所有的參與者節(jié)點(diǎn)發(fā)送abort請求目派,從而中斷事務(wù)。
補(bǔ)償事務(wù)(TCC)
TCC其實(shí)就是采用的補(bǔ)償機(jī)制胁赢,其核心思想是:「針對每個操作址貌,都要注冊一個與其對應(yīng)的確認(rèn)和補(bǔ)償(撤銷)操作」。它分為三個階段:
「Try,Confirm,Cancel」
Try階段主要是對「業(yè)務(wù)系統(tǒng)做檢測及資源預(yù)留」,其主要分為兩個階段
Confirm 階段主要是對「業(yè)務(wù)系統(tǒng)做確認(rèn)提交」练对,Try階段執(zhí)行成功并開始執(zhí)行 Confirm階段時,默認(rèn) Confirm階段是不會出錯的吹害。即:只要Try成功螟凭,Confirm一定成功。
Cancel 階段主要是在業(yè)務(wù)執(zhí)行錯誤它呀,需要回滾的狀態(tài)下執(zhí)行的業(yè)務(wù)取消螺男,「預(yù)留資源釋放」。
比如下一個訂單減一個庫存:
執(zhí)行流程:
Try階段:訂單系統(tǒng)將當(dāng)前訂單狀態(tài)設(shè)置為支付中纵穿,庫存系統(tǒng)校驗(yàn)當(dāng)前剩余庫存數(shù)量是否大于1下隧,然后將可用庫存數(shù)量設(shè)置為庫存剩余數(shù)量-1谓媒,
如果Try階段「執(zhí)行成功」淆院,執(zhí)行Confirm階段土辩,將訂單狀態(tài)修改為支付成功拷淘,庫存剩余數(shù)量修改為可用庫存數(shù)量
如果Try階段「執(zhí)行失敗」,執(zhí)行Cancel階段结洼,將訂單狀態(tài)修改為支付失敗,可用庫存數(shù)量修改為庫存剩余數(shù)量
TCC 事務(wù)機(jī)制相比于上面介紹的2PC详恼,解決了其幾個缺點(diǎn):
- 1.「解決了協(xié)調(diào)者單點(diǎn)」补君,由主業(yè)務(wù)方發(fā)起并完成這個業(yè)務(wù)活動。業(yè)務(wù)活動管理器也變成多點(diǎn)昧互,引入集群挽铁。
- 2.「同步阻塞」:引入超時,超時后進(jìn)行補(bǔ)償敞掘,并且不會鎖定整個資源叽掘,將資源轉(zhuǎn)換為業(yè)務(wù)邏輯形式,粒度變小玖雁。
- 3.「數(shù)據(jù)一致性」更扁,有了補(bǔ)償機(jī)制之后,由業(yè)務(wù)活動管理器控制一致性
總之,TCC 就是通過代碼人為實(shí)現(xiàn)了兩階段提交浓镜,不同的業(yè)務(wù)場景所寫的代碼都不一樣溃列,并且很大程度的「增加」了業(yè)務(wù)代碼的「復(fù)雜度」,因此膛薛,這種模式并不能很好地被復(fù)用听隐。
本地消息表
執(zhí)行流程:
消息生產(chǎn)方,需要額外建一個消息表哄啄,并「記錄消息發(fā)送狀態(tài)」雅任。消息表和業(yè)務(wù)數(shù)據(jù)要在一個事務(wù)里提交,也就是說他們要在一個數(shù)據(jù)庫里面咨跌。然后消息會經(jīng)過MQ發(fā)送到消息的消費(fèi)方沪么。
如果消息發(fā)送失敗,會進(jìn)行重試發(fā)送锌半。
消息消費(fèi)方禽车,需要「處理」這個「消息」,并完成自己的業(yè)務(wù)邏輯拳喻。
如果是「業(yè)務(wù)上面的失敗」哭当,可以給生產(chǎn)方「發(fā)送一個業(yè)務(wù)補(bǔ)償消息」,通知生產(chǎn)方進(jìn)行回滾等操作冗澈。
此時如果本地事務(wù)處理成功钦勘,表明已經(jīng)處理成功了
如果處理失敗,那么就會重試執(zhí)行亚亲。
生產(chǎn)方和消費(fèi)方定時掃描本地消息表彻采,把還沒處理完成的消息或者失敗的消息再發(fā)送一遍。
消息事務(wù)
消息事務(wù)的原理是將兩個事務(wù)「通過消息中間件進(jìn)行異步解耦」捌归,和上述的本地消息表有點(diǎn)類似肛响,但是是通過消息中間件的機(jī)制去做的,其本質(zhì)就是'將本地消息表封裝到了消息中間件中'惜索。
執(zhí)行流程:
發(fā)送prepare消息到消息中間件
發(fā)送成功后特笋,執(zhí)行本地事務(wù)
如果事務(wù)執(zhí)行成功,則commit巾兆,消息中間件將消息下發(fā)至消費(fèi)端
如果事務(wù)執(zhí)行失敗猎物,則回滾,消息中間件將這條prepare消息刪除
消費(fèi)端接收到消息進(jìn)行消費(fèi)角塑,如果消費(fèi)失敗蔫磨,則不斷重試
這種方案也是實(shí)現(xiàn)了「最終一致性」,對比本地消息表實(shí)現(xiàn)方案圃伶,不需要再建消息表堤如,「不再依賴本地?cái)?shù)據(jù)庫事務(wù)」了蒲列,所以這種方案更適用于高并發(fā)的場景。目前市面上實(shí)現(xiàn)該方案的「只有阿里的 RocketMQ」搀罢。
最大努力通知
最大努力通知的方案實(shí)現(xiàn)比較簡單蝗岖,適用于一些最終一致性要求較低的業(yè)務(wù)。
執(zhí)行流程:
- 系統(tǒng) A 本地事務(wù)執(zhí)行完之后榔至,發(fā)送個消息到 MQ剪侮;
- 這里會有個專門消費(fèi) MQ 的服務(wù),這個服務(wù)會消費(fèi) MQ 并調(diào)用系統(tǒng) B 的接口洛退;
- 要是系統(tǒng) B 執(zhí)行成功就 ok 了;要是系統(tǒng) B 執(zhí)行失敗了杰标,那么最大努力通知服務(wù)就定時嘗試重新調(diào)用系統(tǒng) B, 反復(fù) N 次兵怯,最后還是不行就放棄。
Sagas 事務(wù)模型
Saga事務(wù)模型又叫做長時間運(yùn)行的事務(wù)
其核心思想是「將長事務(wù)拆分為多個本地短事務(wù)」腔剂,由Saga事務(wù)協(xié)調(diào)器協(xié)調(diào)媒区,如果正常結(jié)束那就正常完成,如果「某個步驟失敗掸犬,則根據(jù)相反順序一次調(diào)用補(bǔ)償操作」袜漩。
Seata框架中一個分布式事務(wù)包含3種角色:
「Transaction Coordinator (TC)」:事務(wù)協(xié)調(diào)器,維護(hù)全局事務(wù)的運(yùn)行狀態(tài)湾碎,負(fù)責(zé)協(xié)調(diào)并驅(qū)動全局事務(wù)的提交或回滾宙攻。「Transaction Manager (TM)」:控制全局事務(wù)的邊界,負(fù)責(zé)開啟一個全局事務(wù)介褥,并最終發(fā)起全局提交或全局回滾的決議座掘。「Resource Manager (RM)」:控制分支事務(wù),負(fù)責(zé)分支注冊柔滔、狀態(tài)匯報(bào)溢陪,并接收事務(wù)協(xié)調(diào)器的指令,驅(qū)動分支(本地)事務(wù)的提交和回滾睛廊。
seata框架「為每一個RM維護(hù)了一張UNDO_LOG表」形真,其中保存了每一次本地事務(wù)的回滾數(shù)據(jù)。
具體流程:1.首先TM 向 TC 申請「開啟一個全局事務(wù)」超全,全局事務(wù)「創(chuàng)建」成功并生成一個「全局唯一的 XID」咆霜。
2.XID 在微服務(wù)調(diào)用鏈路的上下文中傳播。
3.RM 開始執(zhí)行這個分支事務(wù)卵迂,RM首先解析這條SQL語句裕便,「生成對應(yīng)的UNDO_LOG記錄」。下面是一條UNDO_LOG中的記錄见咒,UNDO_LOG表中記錄了分支ID偿衰,全局事務(wù)ID,以及事務(wù)執(zhí)行的redo和undo數(shù)據(jù)以供二階段恢復(fù)。
4.RM在同一個本地事務(wù)中「執(zhí)行業(yè)務(wù)SQL和UNDO_LOG數(shù)據(jù)的插入」下翎。在提交這個本地事務(wù)前缤言,RM會向TC「申請關(guān)于這條記錄的全局鎖」。
如果申請不到视事,則說明有其他事務(wù)也在對這條記錄進(jìn)行操作胆萧,因此它會在一段時間內(nèi)重試,重試失敗則回滾本地事務(wù)俐东,并向TC匯報(bào)本地事務(wù)執(zhí)行失敗跌穗。
6.RM在事務(wù)提交前,「申請到了相關(guān)記錄的全局鎖」虏辫,然后直接提交本地事務(wù)蚌吸,并向TC「匯報(bào)本地事務(wù)執(zhí)行成功」。此時全局鎖并沒有釋放砌庄,全局鎖的釋放取決于二階段是提交命令還是回滾命令羹唠。
7.TC根據(jù)所有的分支事務(wù)執(zhí)行結(jié)果,向RM「下發(fā)提交或回滾」命令娄昆。
RM如果「收到TC的提交命令」佩微,首先「立即釋放」相關(guān)記錄的全局「鎖」,然后把提交請求放入一個異步任務(wù)的隊(duì)列中萌焰,馬上返回提交成功的結(jié)果給 TC哺眯。異步隊(duì)列中的提交請求真正執(zhí)行時,只是刪除相應(yīng) UNDO LOG 記錄而已杆怕。
RM如果「收到TC的回滾命令」族购,則會開啟一個本地事務(wù),通過 XID 和 Branch ID 查找到相應(yīng)的 UNDO LOG 記錄陵珍。將 UNDO LOG 中的后鏡與當(dāng)前數(shù)據(jù)進(jìn)行比較寝杖,
如果不同,說明數(shù)據(jù)被當(dāng)前全局事務(wù)之外的動作做了修改互纯。這種情況瑟幕,需要根據(jù)配置策略來做處理。
如果相同留潦,根據(jù) UNDO LOG 中的前鏡像和業(yè)務(wù) SQL 的相關(guān)信息生成并執(zhí)行回滾的語句并執(zhí)行只盹,然后提交本地事務(wù)達(dá)到回滾的目的,最后釋放相關(guān)記錄的全局鎖兔院。
總結(jié)
本文介紹了分布式事務(wù)的一些基礎(chǔ)理論殖卑,并對常用的分布式事務(wù)方案進(jìn)行了講解。
分布式事務(wù)本身就是一個技術(shù)難題坊萝,業(yè)務(wù)中具體使用哪種方案還是需要不同的業(yè)務(wù)特點(diǎn)自行選擇孵稽,但是我們也會發(fā)現(xiàn)许起,分布式事務(wù)會大大的提高流程的復(fù)雜度,會帶來很多額外的開銷工作菩鲜,「代碼量上去了园细,業(yè)務(wù)復(fù)雜了,性能下跌了」接校。
所以猛频,當(dāng)我們真實(shí)開發(fā)的過程中,能不使用分布式事務(wù)就不使用蛛勉。