相信很多小伙伴在閱讀分布式事務(wù)相關(guān)文章時虎敦,都有碰到過,上來就是分析各種解決方案(全局事務(wù)身隐、基于可靠消息、最大努力通知唯灵、TCC)贾铝,又摻雜著兩階段提交協(xié)議2PC/TCC(提個小問題2PC和TCC的關(guān)系?)看似內(nèi)容豐滿,但看完之后沒有條理埠帕,記不住這么多垢揩。今天從分布式事務(wù)4種模式的角度,來聊聊分布式事務(wù)理論的發(fā)展及其模式的迭代敛瓷。(再往上層走叁巨,才是具體實現(xiàn),具體實現(xiàn)都是必然的事)
常見分布式事務(wù)解決方案
1呐籽、seata 阿里分布式事務(wù)框架
2锋勺、消息隊列
3、saga
4绝淡、XA
他們有一個共同點宙刘,都是“兩階段”±谓停“兩階段”是指完成整個分布式事務(wù)悬包,劃分成兩個步驟完成。
實際上馍乙,這四種常見的分布式事務(wù)解決方案布近,分別對應(yīng)著分布式事務(wù)的四種模式:AT垫释、TCC、Saga撑瞧、XA棵譬;
四種分布式事務(wù)模式,都有各自的理論基礎(chǔ)预伺,分別在不同的時間被提出订咸;每種模式都有它的適用場景,同樣每個模式也都誕生有各自的代表產(chǎn)品酬诀。
今天脏嚷,我們會分別來看4種模式(AT、TCC瞒御、Saga父叙、XA)的分布式事務(wù)實現(xiàn)。
在看具體實現(xiàn)之前肴裙,先回顧下分布式事務(wù)的理論基礎(chǔ)趾唱。
分布式事務(wù)理論基礎(chǔ)
解決分布式事務(wù),也有相應(yīng)的規(guī)范和協(xié)議蜻懦。分布式事務(wù)相關(guān)的協(xié)議有2PC甜癞、3PC。
由于三階段提交協(xié)議3PC
非常難實現(xiàn)宛乃,目前市面主流的分布式事務(wù)解決方案都是2PC協(xié)議带欢。這就是文章開始提及的常見分布式事務(wù)解決方案里面,那些列舉的都有一個共同點“兩階段”的內(nèi)在原因烤惊。
有些文章分析2PC時,幾乎都會用TCC兩階段的例子吁朦,第一階段try柒室,第二階段完成confirm或cancel。其實2PC并不是專為實現(xiàn)TCC設(shè)計的逗宜,2PC具有普適性——協(xié)議一樣的存在雄右,目前絕大多數(shù)分布式解決方案都是以兩階段提交協(xié)議2PC為基礎(chǔ)的。
TCC(Try-Confirm-Cancel) 實際上是服務(wù)化的兩階段提交協(xié)議纺讲。
2PC兩階段提交協(xié)議
兩階段提交協(xié)議:事務(wù)管理器分兩個階段來協(xié)調(diào)資源管理器擂仍,第一階段準備資源,也就是預(yù)留事務(wù)所需的資源熬甚,如果每個資源管理器都資源預(yù)留成功逢渔,則進行第二階段資源提交,否則協(xié)調(diào)資源管理器回滾資源乡括。
2PC協(xié)議的核心是肃廓,劃分出了事務(wù)參與者和協(xié)調(diào)者的角色智厌,并將整個過程劃分成兩個階段。
第一階段:所有事務(wù)參與者盲赊,執(zhí)行后進行預(yù)提交铣鹏;直到協(xié)調(diào)者收到所有參與者的預(yù)提交才會進入第二步;
- 如果在協(xié)調(diào)者的超時時間內(nèi)哀蘑,有任意參與者的預(yù)提交preCommit沒發(fā)送或未到達诚卸,都會結(jié)束事務(wù)。
第二階段:所有事務(wù)預(yù)提交了各自的結(jié)果后绘迁,由協(xié)調(diào)者決定最終事務(wù)是成功(commit)還是失敗(rollback)合溺。
二階段提交看起來確實能夠提供原子性的操作,但是不幸的事脊髓,二階段提交還是有幾個缺點的:
1.執(zhí)行過程中辫愉,所有參與節(jié)點都是事務(wù)阻塞型的。當(dāng)參與者占有公共資源時将硝,其他第三方節(jié)點訪問公共資源不得不處于阻塞狀態(tài)恭朗。
2.參與者發(fā)生故障。協(xié)調(diào)者需要給每個參與者額外指定超時機制依疼,超時后整個事務(wù)失敗痰腮。(沒有多少容錯機制)
3.協(xié)調(diào)者發(fā)生故障。參與者會一直阻塞下去律罢。需要額外的備機進行容錯膀值。(這個可以依賴后面要講的Paxos協(xié)議實現(xiàn)HA)
4.二階段無法解決的問題:協(xié)調(diào)者再發(fā)出commit消息之后宕機,而唯一接收到這條消息的參與者同時也宕機了误辑。那么即使協(xié)調(diào)者通過選舉協(xié)議產(chǎn)生了新的協(xié)調(diào)者沧踏,這條事務(wù)的狀態(tài)也是不確定的,沒人知道事務(wù)是否被已經(jīng)提交巾钉。
為此翘狱,Dale Skeen和Michael Stonebraker在“A Formal Model of Crash Recovery in a Distributed System”中提出了三階段提交協(xié)議(3PC)。
三階段提交協(xié)議 3PC
與兩階段提交不同的是砰苍,三階段提交有兩個改動點潦匈。
- 引入超時機制。同時在協(xié)調(diào)者和參與者中都引入超時機制赚导。
- 在第一階段和第二階段中插入一個準備階段茬缩。保證了在最后提交階段之前各參與節(jié)點的狀態(tài)是一致的。
也就是說吼旧,除了引入超時機制之外凰锡,3PC把2PC的準備階段再次一分為二,這樣三階段提交就有CanCommit、PreCommit寡夹、DoCommit三個階段处面。
1. CanCommit階段
3PC的CanCommit階段其實和2PC的準備階段很像。協(xié)調(diào)者向參與者發(fā)送commit請求菩掏,參與者如果可以提交就返回Yes響應(yīng)魂角,否則返回No響應(yīng)。
1.事務(wù)詢問 協(xié)調(diào)者向參與者發(fā)送CanCommit請求智绸。詢問是否可以執(zhí)行事務(wù)提交操作野揪。然后開始等待參與者的響應(yīng)。
2.響應(yīng)反饋 參與者接到CanCommit請求之后瞧栗,正常情況下斯稳,如果其自身認為可以順利執(zhí)行事務(wù),則返回Yes響應(yīng)迹恐,并進入預(yù)備狀態(tài)挣惰。否則反饋No
2. PreCommit階段
協(xié)調(diào)者根據(jù)參與者的反應(yīng)情況來決定是否可以繼續(xù)事務(wù)的PreCommit操作。根據(jù)響應(yīng)情況殴边,有以下兩種可能憎茂。 假如協(xié)調(diào)者從所有的參與者獲得的反饋都是Yes響應(yīng),那么就會執(zhí)行事務(wù)的預(yù)執(zhí)行锤岸。
1.發(fā)送預(yù)提交請求 協(xié)調(diào)者向參與者發(fā)送PreCommit請求竖幔,并進入Prepared階段。
2.事務(wù)預(yù)提交 參與者接收到PreCommit請求后是偷,會執(zhí)行事務(wù)操作拳氢,并將undo和redo信息記錄到事務(wù)日志中。
3.響應(yīng)反饋 如果參與者成功的執(zhí)行了事務(wù)操作蛋铆,則返回ACK響應(yīng)馋评,同時開始等待最終指令。
假如有任何一個參與者向協(xié)調(diào)者發(fā)送了No響應(yīng)刺啦,或者等待超時之后栗恩,協(xié)調(diào)者都沒有接到參與者的響應(yīng),那么就執(zhí)行事務(wù)的中斷洪燥。
1.發(fā)送中斷請求 協(xié)調(diào)者向所有參與者發(fā)送abort請求。
2.中斷事務(wù) 參與者收到來自協(xié)調(diào)者的abort請求之后(或超時之后乳乌,仍未收到協(xié)調(diào)者的請求)捧韵,執(zhí)行事務(wù)的中斷。
3. doCommit階段
該階段進行真正的事務(wù)提交汉操,也可以分為以下兩種情況再来。
3.1 執(zhí)行提交
1.發(fā)送提交請求 協(xié)調(diào)接收到參與者發(fā)送的ACK響應(yīng),那么他將從預(yù)提交狀態(tài)進入到提交狀態(tài)。并向所有參與者發(fā)送doCommit請求芒篷。
2.事務(wù)提交 參與者接收到doCommit請求之后搜变,執(zhí)行正式的事務(wù)提交。并在完成事務(wù)提交之后釋放所有事務(wù)資源针炉。
3.響應(yīng)反饋 事務(wù)提交完之后挠他,向協(xié)調(diào)者發(fā)送Ack響應(yīng)。
4.完成事務(wù) 協(xié)調(diào)者接收到所有參與者的ack響應(yīng)之后篡帕,完成事務(wù)殖侵。
3.2 中斷事務(wù)
協(xié)調(diào)者沒有接收到參與者發(fā)送的ACK響應(yīng)(可能是接受者發(fā)送的不是ACK響應(yīng),也可能響應(yīng)超時)镰烧,那么就會執(zhí)行中斷事務(wù)拢军。
1.發(fā)送中斷請求 協(xié)調(diào)者向所有參與者發(fā)送abort請求
2.事務(wù)回滾 參與者接收到abort請求之后,利用其在階段二記錄的undo信息來執(zhí)行事務(wù)的回滾操作怔鳖,并在完成回滾之后釋放所有的事務(wù)資源茉唉。
3.反饋結(jié)果 參與者完成事務(wù)回滾之后,向協(xié)調(diào)者發(fā)送ACK消息
4.中斷事務(wù) 協(xié)調(diào)者接收到參與者反饋的ACK消息之后结执,執(zhí)行事務(wù)的中斷度陆。
下面我們分別來看4種模式(AT、TCC昌犹、Saga坚芜、XA)的分布式事務(wù)實現(xiàn)。
AT模式
AT 模式是一種無侵入的分布式事務(wù)解決方案斜姥。
阿里seata框架鸿竖,實現(xiàn)了該模式。
在 AT 模式下铸敏,用戶只需關(guān)注自己的“業(yè)務(wù) SQL”缚忧,用戶的 “業(yè)務(wù) SQL” 作為一階段,Seata 框架會自動生成事務(wù)的二階段提交和回滾操作杈笔。
AT 模式如何做到對業(yè)務(wù)的無侵入 :
- 一階段:
在一階段闪水,Seata 會攔截“業(yè)務(wù) SQL”,首先解析 SQL 語義蒙具,找到“業(yè)務(wù) SQL”要更新的業(yè)務(wù)數(shù)據(jù)球榆,在業(yè)務(wù)數(shù)據(jù)被更新前,將其保存成“before image”禁筏,然后執(zhí)行“業(yè)務(wù) SQL”更新業(yè)務(wù)數(shù)據(jù)持钉,在業(yè)務(wù)數(shù)據(jù)更新之后,再將其保存成“after image”篱昔,最后生成行鎖每强。以上操作全部在一個數(shù)據(jù)庫事務(wù)內(nèi)完成始腾,這樣保證了一階段操作的原子性。
- 二階段提交:
二階段如果是提交的話空执,因為“業(yè)務(wù) SQL”在一階段已經(jīng)提交至數(shù)據(jù)庫浪箭, 所以 Seata 框架只需將一階段保存的快照數(shù)據(jù)和行鎖刪掉,完成數(shù)據(jù)清理即可辨绊。
- 二階段回滾:
二階段如果是回滾的話奶栖,Seata 就需要回滾一階段已經(jīng)執(zhí)行的“業(yè)務(wù) SQL”,還原業(yè)務(wù)數(shù)據(jù)邢羔⊥漳ǎ回滾方式便是用“before image”還原業(yè)務(wù)數(shù)據(jù);但在還原前要首先要校驗臟寫拜鹤,對比“數(shù)據(jù)庫當(dāng)前業(yè)務(wù)數(shù)據(jù)”和 “after image”框冀,如果兩份數(shù)據(jù)完全一致就說明沒有臟寫,可以還原業(yè)務(wù)數(shù)據(jù)敏簿,如果不一致就說明有臟寫明也,出現(xiàn)臟寫就需要轉(zhuǎn)人工處理。
AT 模式的一階段惯裕、二階段提交和回滾均由 Seata 框架自動生成温数,用戶只需編寫“業(yè)務(wù) SQL”,便能輕松接入分布式事務(wù)蜻势,AT 模式是一種對業(yè)務(wù)無任何侵入的分布式事務(wù)解決方案撑刺。
TCC 模式
TCC 模式需要用戶根據(jù)自己的業(yè)務(wù)場景實現(xiàn) Try、Confirm 和 Cancel 三個操作握玛;事務(wù)發(fā)起方在一階段執(zhí)行 Try 方式够傍,在二階段提交執(zhí)行 Confirm 方法,二階段回滾執(zhí)行 Cancel 方法挠铲。
TCC 三個方法描述:
- Try:資源的檢測和預(yù)留冕屯;
- Confirm:執(zhí)行的業(yè)務(wù)操作提交;要求 Try 成功 Confirm 一定要能成功拂苹;
- Cancel:預(yù)留資源釋放安聘;
TCC 的實踐經(jīng)驗
螞蟻金服TCC實踐,總結(jié)以下注意事項:
?業(yè)務(wù)模型分2階段設(shè)計
?并發(fā)控制
?允許空回滾
?防懸掛控制
?冪等控制
1 TCC 設(shè)計 - 業(yè)務(wù)模型分 2 階段設(shè)計:
用戶接入 TCC ,最重要的是考慮如何將自己的業(yè)務(wù)模型拆成兩階段來實現(xiàn)瓢棒。
以“扣錢”場景為例浴韭,在接入 TCC 前,對 A 賬戶的扣錢脯宿,只需一條更新賬戶余額的 SQL 便能完成念颈;但是在接入 TCC 之后,用戶就需要考慮如何將原來一步就能完成的扣錢操作嗅绰,拆成兩階段,實現(xiàn)成三個方法,并且保證一階段 Try 成功的話 二階段 Confirm 一定能成功窘面。
如上圖所示翠语,Try 方法作為一階段準備方法,需要做資源的檢查和預(yù)留财边。在扣錢場景下肌括,Try 要做的事情是就是檢查賬戶余額是否充足,預(yù)留轉(zhuǎn)賬資金酣难,預(yù)留的方式就是凍結(jié) A 賬戶的 轉(zhuǎn)賬資金谍夭。Try 方法執(zhí)行之后,賬號 A 余額雖然還是 100憨募,但是其中 30 元已經(jīng)被凍結(jié)了紧索,不能被其他事務(wù)使用。
二階段 Confirm 方法執(zhí)行真正的扣錢操作菜谣。Confirm 會使用 Try 階段凍結(jié)的資金珠漂,執(zhí)行賬號扣款。Confirm 方法執(zhí)行之后尾膊,賬號 A 在一階段中凍結(jié)的 30 元已經(jīng)被扣除媳危,賬號 A 余額變成 70 元 。
如果二階段是回滾的話冈敛,就需要在 Cancel 方法內(nèi)釋放一階段 Try 凍結(jié)的 30 元待笑,使賬號 A 的回到初始狀態(tài),100 元全部可用抓谴。
用戶接入 TCC 模式暮蹂,最重要的事情就是考慮如何將業(yè)務(wù)模型拆成 2 階段,實現(xiàn)成 TCC 的 3 個方法齐邦,并且保證 Try 成功 Confirm 一定能成功椎侠。相對于 AT 模式,TCC 模式對業(yè)務(wù)代碼有一定的侵入性措拇,但是 TCC 模式無 AT 模式的全局行鎖我纪,TCC 性能會比 AT 模式高很多。
2 TCC 設(shè)計 - 允許空回滾:
Cancel 接口設(shè)計時需要允許空回滾丐吓。在 Try 接口因為丟包時沒有收到浅悉,事務(wù)管理器會觸發(fā)回滾,這時會觸發(fā) Cancel 接口券犁,這時 Cancel 執(zhí)行時發(fā)現(xiàn)沒有對應(yīng)的事務(wù) xid 或主鍵時术健,需要返回回滾成功。讓事務(wù)服務(wù)管理器認為已回滾粘衬,否則會不斷重試荞估,而 Cancel 又沒有對應(yīng)的業(yè)務(wù)數(shù)據(jù)可以進行回滾咳促。
3 TCC 設(shè)計 - 防懸掛控制:
懸掛的意思是:Cancel 比 Try 接口先執(zhí)行,出現(xiàn)的原因是 Try 由于網(wǎng)絡(luò)擁堵而超時勘伺,事務(wù)管理器生成回滾跪腹,觸發(fā) Cancel 接口,而最終又收到了 Try 接口調(diào)用飞醉,但是 Cancel 比 Try 先到冲茸。按照前面允許空回滾的邏輯,回滾會返回成功缅帘,事務(wù)管理器認為事務(wù)已回滾成功轴术,則此時的 Try 接口不應(yīng)該執(zhí)行,否則會產(chǎn)生數(shù)據(jù)不一致钦无,所以我們在 Cancel 空回滾返回成功之前先記錄該條事務(wù) xid 或業(yè)務(wù)主鍵逗栽,標識這條記錄已經(jīng)回滾過,Try 接口先檢查這條事務(wù)xid或業(yè)務(wù)主鍵如果已經(jīng)標記為回滾成功過铃诬,則不執(zhí)行 Try 的業(yè)務(wù)操作祭陷。
4 TCC 設(shè)計 - 冪等控制:
冪等性的意思是:對同一個系統(tǒng),使用同樣的條件趣席,一次請求和重復(fù)的多次請求對系統(tǒng)資源的影響是一致的兵志。因為網(wǎng)絡(luò)抖動或擁堵可能會超時,事務(wù)管理器會對資源進行重試操作宣肚,所以很可能一個業(yè)務(wù)操作會被重復(fù)調(diào)用想罕,為了不因為重復(fù)調(diào)用而多次占用資源,需要對服務(wù)設(shè)計時進行冪等控制霉涨,通常我們可以用事務(wù) xid 或業(yè)務(wù)主鍵判重來控制按价。
saga模式
Saga 理論出自 Hector & Kenneth 1987發(fā)表的論文 Sagas。
saga模式的實現(xiàn)笙瑟,是長事務(wù)解決方案楼镐。
Saga 是一種補償協(xié)議,在 Saga 模式下往枷,分布式事務(wù)內(nèi)有多個參與者框产,每一個參與者都是一個沖正補償服務(wù),需要用戶根據(jù)業(yè)務(wù)場景實現(xiàn)其正向操作和逆向回滾操作错洁。
如圖:T1~T3都是正向的業(yè)務(wù)流程秉宿,都對應(yīng)著一個沖正逆向操作C1~C3
分布式事務(wù)執(zhí)行過程中,依次執(zhí)行各參與者的正向操作屯碴,如果所有正向操作均執(zhí)行成功描睦,那么分布式事務(wù)提交。如果任何一個正向操作執(zhí)行失敗导而,那么分布式事務(wù)會退回去執(zhí)行前面各參與者的逆向回滾操作忱叭,回滾已提交的參與者隔崎,使分布式事務(wù)回到初始狀態(tài)。
Saga 正向服務(wù)與補償服務(wù)也需要業(yè)務(wù)開發(fā)者實現(xiàn)韵丑。因此是業(yè)務(wù)入侵的仍稀。
Saga 模式下分布式事務(wù)通常是由事件驅(qū)動的,各個參與者之間是異步執(zhí)行的埂息,Saga 模式是一種長事務(wù)解決方案。
Saga 模式使用場景
Saga 模式適用于業(yè)務(wù)流程長且需要保證事務(wù)最終一致性的業(yè)務(wù)系統(tǒng)遥巴,Saga 模式一階段就會提交本地事務(wù)千康,無鎖、長流程情況下可以保證性能铲掐。
事務(wù)參與者可能是其它公司的服務(wù)或者是遺留系統(tǒng)的服務(wù)拾弃,無法進行改造和提供 TCC 要求的接口,可以使用 Saga 模式摆霉。
Saga模式的優(yōu)勢是:
- 一階段提交本地數(shù)據(jù)庫事務(wù)豪椿,無鎖,高性能携栋;
- 參與者可以采用事務(wù)驅(qū)動異步執(zhí)行搭盾,高吞吐;
- 補償服務(wù)即正向服務(wù)的“反向”婉支,易于理解鸯隅,易于實現(xiàn);
缺點:Saga 模式由于一階段已經(jīng)提交本地數(shù)據(jù)庫事務(wù)向挖,且沒有進行“預(yù)留”動作蝌以,所以不能保證隔離性。后續(xù)會講到對于缺乏隔離性的應(yīng)對措施何之。
與TCC實踐經(jīng)驗相同的是跟畅,Saga 模式中,每個事務(wù)參與者的沖正溶推、逆向操作徊件,需要支持:
- 空補償:逆向操作早于正向操作時;
- 防懸掛控制:空補償后要拒絕正向操作
- 冪等
XA模式
XA是X/Open DTP組織(X/Open DTP group)定義的兩階段提交協(xié)議悼潭,XA被許多數(shù)據(jù)庫(如Oracle庇忌、DB2、SQL Server舰褪、MySQL)和中間件等工具(如CICS 和 Tuxedo)本地支持 皆疹。
X/Open DTP模型(1994)包括應(yīng)用程序(AP)、事務(wù)管理器(TM)占拍、資源管理器(RM)略就。
XA接口函數(shù)由數(shù)據(jù)庫廠商提供捎迫。XA規(guī)范的基礎(chǔ)是兩階段提交協(xié)議2PC。
JTA(Java Transaction API) 是Java實現(xiàn)的XA規(guī)范的增強版 接口表牢。
在XA模式下窄绒,需要有一個[全局]協(xié)調(diào)器,每一個數(shù)據(jù)庫事務(wù)完成后崔兴,進行第一階段預(yù)提交彰导,并通知協(xié)調(diào)器,把結(jié)果給協(xié)調(diào)器敲茄。協(xié)調(diào)器等所有分支事務(wù)操作完成位谋、都預(yù)提交后,進行第二步堰燎;第二步:協(xié)調(diào)器通知每個數(shù)據(jù)庫進行逐個commit/rollback掏父。
其中,這個全局協(xié)調(diào)器就是XA模型中的TM角色秆剪,每個分支事務(wù)各自的數(shù)據(jù)庫就是RM赊淑。
MySQL 提供的XA實現(xiàn)(https://dev.mysql.com/doc/refman/5.7/en/xa.html )
XA模式下的 開源框架有atomikos,其開發(fā)公司也有商業(yè)版本仅讽。
XA模式缺點:事務(wù)粒度大陶缺。高并發(fā)下,系統(tǒng)可用性低洁灵。因此很少使用组哩。
(AT、TCC处渣、Saga伶贰、XA)模式分析
四種分布式事務(wù)模式,分別在不同的時間被提出罐栈,每種模式都有它的適用場景
- AT 模式是無侵入的分布式事務(wù)解決方案黍衙,適用于不希望對業(yè)務(wù)進行改造的場景,幾乎0學(xué)習(xí)成本荠诬。
- TCC 模式是高性能分布式事務(wù)解決方案琅翻,適用于核心系統(tǒng)等對性能有很高要求的場景。
- Saga 模式是長事務(wù)解決方案柑贞,適用于業(yè)務(wù)流程長且需要保證事務(wù)最終一致性的業(yè)務(wù)系統(tǒng)方椎,Saga 模式一階段就會提交本地事務(wù),無鎖钧嘶,長流程情況下可以保證性能棠众,多用于渠道層、集成層業(yè)務(wù)系統(tǒng)。事務(wù)參與者可能是其它公司的服務(wù)或者是遺留系統(tǒng)的服務(wù)闸拿,無法進行改造和提供 TCC 要求的接口空盼,也可以使用 Saga 模式。
- XA模式是分布式強一致性的解決方案新荤,但性能低而使用較少揽趾。