1 分布式事務(wù)定義
分布式事務(wù)就是指事務(wù)的參與者、支持事務(wù)的服務(wù)器、資源服務(wù)器以及事務(wù)管理器分別位于不同的分布式系統(tǒng)的不同節(jié)點(diǎn)之上。以上是百度百科的解釋,簡單的說,就是一次大的操作由不同的小操作組成,這些小的操作分布在不同的服務(wù)器上,且屬于不同的應(yīng)用,分布式事務(wù)需要保證這些小操作要么全部成功,要么全部失敗。本質(zhì)上來說,分布式事務(wù)就是為了保證不同數(shù)據(jù)庫的數(shù)據(jù)一致性。
2 分布式事務(wù)產(chǎn)生的背景
2.1數(shù)據(jù)庫的分庫分表
當(dāng)數(shù)據(jù)庫單表一年產(chǎn)生的數(shù)據(jù)超過1000W,那么就要考慮分庫分表,具體分庫分表的原理在此不做解釋,以后有空詳細(xì)說,簡單的說就是原來的一個數(shù)據(jù)庫變成了多個數(shù)據(jù)庫。這時候,如果一個操作既訪問01庫,又訪問02庫,而且要保證數(shù)據(jù)的一致性,那么就要用到分布式事務(wù)。

2.2應(yīng)用SOA化
所謂的SOA化,就是業(yè)務(wù)的服務(wù)化。比如原來單機(jī)支撐了整個電商網(wǎng)站,現(xiàn)在對整個網(wǎng)站進(jìn)行拆解,分離出了訂單中心、用戶中心、庫存中心。對于訂單中心,有專門的數(shù)據(jù)庫存儲訂單信息,用戶中心也有專門的數(shù)據(jù)庫存儲用戶信息,庫存中心也會有專門的數(shù)據(jù)庫存儲庫存信息。這時候如果要同時對訂單和庫存進(jìn)行操作,那么就會涉及到訂單數(shù)據(jù)庫和庫存數(shù)據(jù)庫,為了保證數(shù)據(jù)一致性,就需要用到分布式事務(wù)。

3 解決分布式事務(wù)所面臨的問題
- 原子性
整個事務(wù)中的所有操作,要么全部完成,要么全部不完成,不可能停滯在中間某個環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯誤,會被回滾(Rollback)到事務(wù)開始前的狀態(tài),就像這個事務(wù)從來沒有執(zhí)行過一樣。 - 隔離性
對于并發(fā)提交的事務(wù),數(shù)據(jù)庫需要提供并發(fā)控制,保證數(shù)據(jù)庫從一個事務(wù)的開始狀態(tài)轉(zhuǎn)換到該事務(wù)的結(jié)束狀態(tài)的過程中,中間狀態(tài)不被其他事務(wù)可見,就像事務(wù)串行執(zhí)行一樣。 - 持久性
在事務(wù)完成以后,即使發(fā)生掉電、系統(tǒng)宕機(jī)等錯誤,該事務(wù)對數(shù)據(jù)庫所作的更改仍需持久的保存在數(shù)據(jù)庫中。 - 一致性
從事務(wù)開始到事務(wù)結(jié)束,數(shù)據(jù)庫的完整性約束(包括外建約束、級連關(guān)系、觸發(fā)器等)不應(yīng)該因?yàn)槿魏卧虮黄茐摹?/li>
4 典型分布式事務(wù)場景
4.1 支付
一筆支付,是對買家賬戶進(jìn)行扣款,同時對賣家賬戶進(jìn)行加錢,這些操作必須在一個事務(wù)里執(zhí)行,要么全部成功,要么全部失敗。而對于買家賬戶屬于買家中心,對應(yīng)的是買家數(shù)據(jù)庫,而賣家賬戶屬于賣家中心,對應(yīng)的是賣家數(shù)據(jù)庫,對不同數(shù)據(jù)庫的操作必然需要引入分布式事務(wù)。
4.2 下單減庫存
在消費(fèi)者點(diǎn)擊購買按鈕后,交易后臺會進(jìn)行庫存檢查、下單、減庫存、更新訂單狀態(tài)等一連串的服務(wù)調(diào)用,每一個操作對應(yīng)一個獨(dú)立的服務(wù),服務(wù)一般會有獨(dú)立的數(shù)據(jù)庫,因此產(chǎn)生分布式事務(wù)問題。
5 常用解決方案
5.1 基于XA協(xié)議的兩階段提交

優(yōu)點(diǎn):
- XA接口標(biāo)準(zhǔn)化、使用簡單,使用成本也很低;
缺點(diǎn): - 性能不理想,很難滿足互聯(lián)網(wǎng)大并發(fā)需求。開銷大,分布式事務(wù)RT相較于單機(jī)事務(wù)有數(shù)量級的差距;分布式事務(wù)對相關(guān)資源的加鎖機(jī)制增加了并發(fā)沖突,影響到系統(tǒng)吞吐和可伸縮性;
- XA要求資源管理必須實(shí)現(xiàn)XA接口,對資源管理器提出了要求,限制了業(yè)務(wù)方的產(chǎn)品選型;
- 在Mysql數(shù)據(jù)庫中支持的不太理想,mysql的XA實(shí)現(xiàn),沒有記錄prepare階段日志,主備切換回導(dǎo)致主庫與備庫數(shù)據(jù)不一致。許多nosql也沒有支持XA,這讓XA的應(yīng)用場景變得非常狹隘。
5.2 消息事務(wù)+最終一致性
5.2.1 業(yè)務(wù)與消息耦合
在執(zhí)行服務(wù)A時,同時記錄消息數(shù)據(jù),這個消息數(shù)據(jù)和服務(wù)A的業(yè)務(wù)數(shù)據(jù)保存到同一個數(shù)據(jù)庫實(shí)例中。
進(jìn)程1:
Begin local trx:
服務(wù)A DML;
push messageQ “call 服務(wù)B”;
Commit/Rollback;
進(jìn)程2:
for eash message in queue
begin local trx:
服務(wù)B DML;
commit/rollback;
if trx.success:
pop message;
end if;
end for;
進(jìn)程1中,由于消息隊(duì)列和服務(wù)A共用數(shù)據(jù)庫資源,可以把消息和服務(wù)在單機(jī)事務(wù)中執(zhí)行;進(jìn)程2中,如果服務(wù)B執(zhí)行失敗,重新消費(fèi)消息重試即可;進(jìn)程2中,如果服務(wù)B執(zhí)行成功、刪除消息失敗,如果服務(wù)B能夠?qū)崿F(xiàn)冪等然后可以重試(可以改造服務(wù)B,執(zhí)行B的事務(wù)中,同步插入一行記錄描述B是否執(zhí)行,重試時檢查下即可消除對冪等的要求)。
優(yōu)點(diǎn):滿足業(yè)務(wù)最終一致要求的同時,有高性能;
缺點(diǎn):消息系統(tǒng)和資源管理器需要共用數(shù)據(jù)庫資源,不利于業(yè)務(wù)分層;系統(tǒng)延遲高,沒有隔離性;一旦消息產(chǎn)生,后續(xù)業(yè)務(wù)不允許失敗,一旦中間某個階段出現(xiàn)無法繼續(xù)的情況,即需要人工介入;系統(tǒng)開發(fā)成本高,開發(fā)人員需要關(guān)注分布式事務(wù)對業(yè)務(wù)的影響以及各種狀態(tài)轉(zhuǎn)換,測試?yán)щy,一般來說,開發(fā)成本會是不考慮事務(wù)的許多倍。
5.2.2 業(yè)務(wù)與消息解耦
上述方案中,消息資源和業(yè)務(wù)共用數(shù)據(jù)庫資源,無法解決調(diào)用多于2個服務(wù)的業(yè)務(wù)場景,無法應(yīng)對業(yè)務(wù)執(zhí)行順序發(fā)生變更的需求。因此,考慮將業(yè)務(wù)資源和消息資源解耦。
進(jìn)程1:
push half message;
Begin local trx:
服務(wù)A DML;
Commit/Rollback;
If trx.success
Commit half message;
End if;
進(jìn)程2:
for eash message in queue
begin local trx:
服務(wù)B DML;
commit/rollback;
if trx.success:
pop message;
end if;
end for;
進(jìn)程1中,首先發(fā)送半消息,該消息還不能被消費(fèi),然后執(zhí)行服務(wù)A,服務(wù)A執(zhí)行成功后,使半消息生效,否則刪除半消息。進(jìn)程2的執(zhí)行邏輯與上面相同。
如果服務(wù)A執(zhí)行成功后、使半消息生效前,進(jìn)程1crash,消息系統(tǒng)會看到一條半消息,但是不知道服務(wù)A是否執(zhí)行成功,所以要求業(yè)務(wù)實(shí)現(xiàn)回調(diào)接口,用于檢查服務(wù)A是否執(zhí)行成功。
優(yōu)點(diǎn):滿足業(yè)務(wù)最終一致的同時,實(shí)現(xiàn)高性能;消息數(shù)據(jù)獨(dú)立存儲,消除業(yè)務(wù)系統(tǒng)與消息系統(tǒng)間的耦合;
缺點(diǎn):一次消息發(fā)送需要兩次請求;業(yè)務(wù)處理服務(wù)需要實(shí)現(xiàn)消息狀態(tài)回查接口;系統(tǒng)延遲高,沒有隔離性;一旦消息產(chǎn)生,后續(xù)業(yè)務(wù)不允許失敗,一旦中間某個階段出現(xiàn)無法繼續(xù)的情況,即需要人工接入;系統(tǒng)開發(fā)成本高,開發(fā)人員需要關(guān)注分布式事務(wù)對業(yè)務(wù)的影響以及各種狀態(tài)轉(zhuǎn)換,測試?yán)щy,一般來說,開發(fā)成本會是不考慮事務(wù)的許多倍。
6 分布式事務(wù)系統(tǒng)解決方案
6.1整體架構(gòu)
系統(tǒng)分為三種角色:
- 客戶端
客戶端通過事務(wù)協(xié)調(diào)器開啟/提交分布式事務(wù),通過資源管理器執(zhí)行業(yè)務(wù) - 資源管理器
資源管理器(主要是數(shù)據(jù)庫系統(tǒng)、消息系統(tǒng)等)負(fù)責(zé)具體的資源操作,記錄必要的事務(wù)日志并將執(zhí)行狀態(tài)匯報(bào)給事務(wù)協(xié)調(diào)器 - 事務(wù)協(xié)調(diào)器
事務(wù)協(xié)調(diào)器負(fù)責(zé)分布式事務(wù)的推進(jìn),為客戶端發(fā)起的分布式事務(wù)請求分配全局唯一的事務(wù)ID,并記錄資源管理器提交的事務(wù)分支的狀態(tài),最終負(fù)責(zé)全局事務(wù)的提交或回滾
分布式事務(wù)系統(tǒng)通過兩階段提交方式進(jìn)行分布式事務(wù)推進(jìn)。
1)客戶端向事務(wù)協(xié)調(diào)器注冊全局事務(wù)作為一階段的開啟的標(biāo)記;
2)分布式事務(wù)內(nèi)的每一次資源(DB或消息)操作,均通過資源管理器進(jìn)行,資源管理器向事務(wù)協(xié)調(diào)器注冊一個事務(wù)分支;
3)客戶端通知事務(wù)協(xié)調(diào)器進(jìn)行全局提交/全局回滾作為一階段完成的標(biāo)記

分布式事務(wù)的二階段由事務(wù)協(xié)調(diào)器驅(qū)動,驅(qū)動所有事務(wù)分支執(zhí)行提交或回滾操作,一旦確定某個分布式事務(wù)提交或回滾,則不斷重試所有事務(wù)分支,直到完成整個分布式事務(wù)提交或回滾

6.2 系統(tǒng)的結(jié)構(gòu)
需要提供提供多種資源器支持,在接入層,服務(wù)化框架自動加入全局事務(wù);在資源管理層,需要提供數(shù)據(jù)庫分庫分表組件及支持事務(wù)消息的消息系統(tǒng),如:RabbitMQ、RocketMQ等

6.3 系統(tǒng)時序圖

6.4 與XA兩階段提交的不同之處
- XA依賴于數(shù)據(jù)庫的XA接口
- XA在第一階段沒有提交本地事務(wù),而事務(wù)中間件立即執(zhí)行并可見
- XA在第二階段提交各分支事務(wù),而事務(wù)中間件清理各分支事務(wù)的Undo/Redo日志
- XA依賴于數(shù)據(jù)庫的Rollback接口來回滾事務(wù),而事務(wù)中間件通過Undo/Redo日志來實(shí)現(xiàn)分支事務(wù)的回滾