分佈式事務的4種模式

在這裏插入圖片描述

相信很多小夥伴在閱讀分佈式事務相關文章時,都有碰到過,上來就是分析各種解決方案(全局事務、基於可靠消息、最大努力通知、TCC),又摻雜着兩階段提交協議2PC/TCC(提個小問題2PC和TCC的關係?)
看似內容豐滿,但看完之後沒有條理,記不住這麼多。今天從分佈式事務4種模式的角度,來聊聊分佈式事務理論的發展及其模式的迭代。(再往上層走,纔是具體實現,具體實現都是必然的事)

常見分佈式事務解決方案
1、seata 阿里分佈式事務框架
2、消息隊列
3、saga
4、XA

他們有一個共同點,都是“兩階段”。“兩階段”是指完成整個分佈式事務,劃分成兩個步驟完成。

實際上,這四種常見的分佈式事務解決方案,分別對應着分佈式事務的四種模式:AT、TCC、Saga、XA;

四種分佈式事務模式,都有各自的理論基礎,分別在不同的時間被提出;每種模式都有它的適用場景,同樣每個模式也都誕生有各自的代表產品;而這些代表產品,可能就是我們常見的(全局事務、基於可靠消息、最大努力通知、TCC)。

今天,我們會分別來看4種模式(AT、TCC、Saga、XA)的分佈式事務實現。

在看具體實現之前,先回顧下分佈式事務的理論基礎。


分佈式事務理論基礎

解決分佈式事務,也有相應的規範和協議。分佈式事務相關的協議有2PC、3PC。

由於三階段提交協議3PC非常難實現,目前市面主流的分佈式事務解決方案都是2PC協議。這就是文章開始提及的常見分佈式事務解決方案裏面,那些列舉的都有一個共同點“兩階段”的內在原因。

有些文章分析2PC時,幾乎都會用TCC兩階段的例子,第一階段try,第二階段完成confirm或cancel。其實2PC並不是專爲實現TCC設計的,2PC具有普適性——協議一樣的存在,目前絕大多數分佈式解決方案都是以兩階段提交協議2PC爲基礎的。

TCC(Try-Confirm-Cancel) 實際上是服務化的兩階段提交協議。

2PC兩階段提交協議

兩階段提交協議:事務管理器分兩個階段來協調資源管理器,第一階段準備資源,也就是預留事務所需的資源,如果每個資源管理器都資源預留成功,則進行第二階段資源提交,否則協調資源管理器回滾資源。

2PC協議的核心是,劃分出了事務參與者和協調者的角色,並將整個過程劃分成兩個階段。

第一階段:所有事務參與者,執行後進行預提交;直到協調者收到所有參與者的預提交纔會進入第二步;

  • 如果在協調者的超時時間內,有任意參與者的預提交preCommit沒發送或未到達,都會結束事務。

第二階段:所有事務預提交了各自的結果後,由協調者決定最終事務是成功(commit)還是失敗(rollback)。

二階段提交看起來確實能夠提供原子性的操作,但是不幸的事,二階段提交還是有幾個缺點的:

1.執行過程中,所有參與節點都是事務阻塞型的。當參與者佔有公共資源時,其他第三方節點訪問公共資源不得不處於阻塞狀態。
2.參與者發生故障。協調者需要給每個參與者額外指定超時機制,超時後整個事務失敗。(沒有多少容錯機制)
3.協調者發生故障。參與者會一直阻塞下去。需要額外的備機進行容錯。(這個可以依賴後面要講的Paxos協議實現HA)
4.二階段無法解決的問題:協調者再發出commit消息之後宕機,而唯一接收到這條消息的參與者同時也宕機了。那麼即使協調者通過選舉協議產生了新的協調者,這條事務的狀態也是不確定的,沒人知道事務是否被已經提交。

爲此,Dale Skeen和Michael Stonebraker在“A Formal Model of Crash Recovery in a Distributed System”中提出了三階段提交協議(3PC)。

三階段提交協議 3PC

與兩階段提交不同的是,三階段提交有兩個改動點。

  • 引入超時機制。同時在協調者和參與者中都引入超時機制。
  • 在第一階段和第二階段中插入一個準備階段。保證了在最後提交階段之前各參與節點的狀態是一致的。

也就是說,除了引入超時機制之外,3PC把2PC的準備階段再次一分爲二,這樣三階段提交就有CanCommit、PreCommit、DoCommit三個階段。

1. CanCommit階段

3PC的CanCommit階段其實和2PC的準備階段很像。協調者向參與者發送commit請求,參與者如果可以提交就返回Yes響應,否則返回No響應。
1.事務詢問 協調者向參與者發送CanCommit請求。詢問是否可以執行事務提交操作。然後開始等待參與者的響應。
2.響應反饋 參與者接到CanCommit請求之後,正常情況下,如果其自身認爲可以順利執行事務,則返回Yes響應,並進入預備狀態。否則反饋No

2. PreCommit階段

協調者根據參與者的反應情況來決定是否可以繼續事務的PreCommit操作。根據響應情況,有以下兩種可能。 假如協調者從所有的參與者獲得的反饋都是Yes響應,那麼就會執行事務的預執行。
1.發送預提交請求 協調者向參與者發送PreCommit請求,並進入Prepared階段。
2.事務預提交 參與者接收到PreCommit請求後,會執行事務操作,並將undo和redo信息記錄到事務日誌中。
3.響應反饋 如果參與者成功的執行了事務操作,則返回ACK響應,同時開始等待最終指令。

假如有任何一個參與者向協調者發送了No響應,或者等待超時之後,協調者都沒有接到參與者的響應,那麼就執行事務的中斷。
1.發送中斷請求 協調者向所有參與者發送abort請求。
2.中斷事務 參與者收到來自協調者的abort請求之後(或超時之後,仍未收到協調者的請求),執行事務的中斷。

3. doCommit階段
該階段進行真正的事務提交,也可以分爲以下兩種情況。
3.1 執行提交
1.發送提交請求 協調接收到參與者發送的ACK響應,那麼他將從預提交狀態進入到提交狀態。並向所有參與者發送doCommit請求。
2.事務提交 參與者接收到doCommit請求之後,執行正式的事務提交。並在完成事務提交之後釋放所有事務資源。
3.響應反饋 事務提交完之後,向協調者發送Ack響應。
4.完成事務 協調者接收到所有參與者的ack響應之後,完成事務。
3.2 中斷事務
協調者沒有接收到參與者發送的ACK響應(可能是接受者發送的不是ACK響應,也可能響應超時),那麼就會執行中斷事務。
1.發送中斷請求 協調者向所有參與者發送abort請求
2.事務回滾 參與者接收到abort請求之後,利用其在階段二記錄的undo信息來執行事務的回滾操作,並在完成回滾之後釋放所有的事務資源。
3.反饋結果 參與者完成事務回滾之後,向協調者發送ACK消息
4.中斷事務 協調者接收到參與者反饋的ACK消息之後,執行事務的中斷。


下面我們分別來看4種模式(AT、TCC、Saga、XA)的分佈式事務實現。

AT模式

AT 模式是一種無侵入的分佈式事務解決方案。
阿里seata框架,實現了該模式。

在 AT 模式下,用戶只需關注自己的“業務 SQL”,用戶的 “業務 SQL” 作爲一階段,Seata 框架會自動生成事務的二階段提交和回滾操作。

AT 模式如何做到對業務的無侵入 :

  • 一階段:
    在一階段,Seata 會攔截“業務 SQL”,首先解析 SQL 語義,找到“業務 SQL”要更新的業務數據,在業務數據被更新前,將其保存成“before image”,然後執行“業務 SQL”更新業務數據,在業務數據更新之後,再將其保存成“after image”,最後生成行鎖。以上操作全部在一個數據庫事務內完成,這樣保證了一階段操作的原子性。

  • 二階段提交:
    二階段如果是提交的話,因爲“業務 SQL”在一階段已經提交至數據庫, 所以 Seata 框架只需將一階段保存的快照數據和行鎖刪掉,完成數據清理即可。

  • 二階段回滾:
    二階段如果是回滾的話,Seata 就需要回滾一階段已經執行的“業務 SQL”,還原業務數據。回滾方式便是用“before image”還原業務數據;但在還原前要首先要校驗髒寫,對比“數據庫當前業務數據”和 “after image”,如果兩份數據完全一致就說明沒有髒寫,可以還原業務數據,如果不一致就說明有髒寫,出現髒寫就需要轉人工處理。

AT 模式的一階段、二階段提交和回滾均由 Seata 框架自動生成,用戶只需編寫“業務 SQL”,便能輕鬆接入分佈式事務,AT 模式是一種對業務無任何侵入的分佈式事務解決方案。

TCC 模式

TCC 模式需要用戶根據自己的業務場景實現 Try、Confirm 和 Cancel 三個操作;事務發起方在一階段執行 Try 方式,在二階段提交執行 Confirm 方法,二階段回滾執行 Cancel 方法。

TCC 三個方法描述:

  • Try:資源的檢測和預留;
  • Confirm:執行的業務操作提交;要求 Try 成功 Confirm 一定要能成功;
  • Cancel:預留資源釋放;
TCC 的實踐經驗

螞蟻金服TCC實踐,總結以下注意事項:

➢業務模型分2階段設計
➢併發控制
➢允許空回滾
➢防懸掛控制
➢冪等控制

1 TCC 設計 - 業務模型分 2 階段設計:
用戶接入 TCC ,最重要的是考慮如何將自己的業務模型拆成兩階段來實現。

以“扣錢”場景爲例,在接入 TCC 前,對 A 賬戶的扣錢,只需一條更新賬戶餘額的 SQL 便能完成;但是在接入 TCC 之後,用戶就需要考慮如何將原來一步就能完成的扣錢操作,拆成兩階段,實現成三個方法,並且保證一階段 Try  成功的話 二階段 Confirm 一定能成功。

如上圖所示,Try 方法作爲一階段準備方法,需要做資源的檢查和預留。在扣錢場景下,Try 要做的事情是就是檢查賬戶餘額是否充足,預留轉賬資金,預留的方式就是凍結 A 賬戶的 轉賬資金。Try 方法執行之後,賬號 A 餘額雖然還是 100,但是其中 30 元已經被凍結了,不能被其他事務使用。

二階段 Confirm 方法執行真正的扣錢操作。Confirm 會使用 Try 階段凍結的資金,執行賬號扣款。Confirm 方法執行之後,賬號 A 在一階段中凍結的 30 元已經被扣除,賬號 A 餘額變成 70 元 。

如果二階段是回滾的話,就需要在 Cancel 方法內釋放一階段 Try 凍結的 30 元,使賬號 A 的回到初始狀態,100 元全部可用。

用戶接入 TCC 模式,最重要的事情就是考慮如何將業務模型拆成 2 階段,實現成 TCC 的 3 個方法,並且保證 Try 成功 Confirm 一定能成功。相對於 AT 模式,TCC 模式對業務代碼有一定的侵入性,但是 TCC 模式無 AT 模式的全局行鎖,TCC 性能會比 AT 模式高很多。

2 TCC 設計 - 允許空回滾:

Cancel 接口設計時需要允許空回滾。在 Try 接口因爲丟包時沒有收到,事務管理器會觸發回滾,這時會觸發 Cancel 接口,這時 Cancel 執行時發現沒有對應的事務 xid 或主鍵時,需要返回回滾成功。讓事務服務管理器認爲已回滾,否則會不斷重試,而 Cancel 又沒有對應的業務數據可以進行回滾。

3 TCC 設計 - 防懸掛控制:

[外鏈圖片轉存失敗(img-EdnVgi3F-1566224376806)(https://upload-images.jianshu.io/upload_images/9033085-339dca209f9fcd1d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/550)]

懸掛的意思是:Cancel 比 Try 接口先執行,出現的原因是 Try 由於網絡擁堵而超時,事務管理器生成回滾,觸發 Cancel 接口,而最終又收到了 Try 接口調用,但是 Cancel 比 Try 先到。按照前面允許空回滾的邏輯,回滾會返回成功,事務管理器認爲事務已回滾成功,則此時的 Try 接口不應該執行,否則會產生數據不一致,所以我們在 Cancel 空回滾返回成功之前先記錄該條事務 xid 或業務主鍵,標識這條記錄已經回滾過,Try 接口先檢查這條事務xid或業務主鍵如果已經標記爲回滾成功過,則不執行 Try 的業務操作。

4 TCC 設計 - 冪等控制:

冪等性的意思是:對同一個系統,使用同樣的條件,一次請求和重複的多次請求對系統資源的影響是一致的。因爲網絡抖動或擁堵可能會超時,事務管理器會對資源進行重試操作,所以很可能一個業務操作會被重複調用,爲了不因爲重複調用而多次佔用資源,需要對服務設計時進行冪等控制,通常我們可以用事務 xid 或業務主鍵判重來控制。

saga模式

Saga 理論出自 Hector & Kenneth 1987發表的論文 Sagas。
saga模式的實現,是長事務解決方案。

Saga 是一種補償協議,在 Saga 模式下,分佈式事務內有多個參與者,每一個參與者都是一個衝正補償服務,需要用戶根據業務場景實現其正向操作和逆向回滾操作。

如圖:T1~T3都是正向的業務流程,都對應着一個衝正逆向操作C1~C3

分佈式事務執行過程中,依次執行各參與者的正向操作,如果所有正向操作均執行成功,那麼分佈式事務提交。如果任何一個正向操作執行失敗,那麼分佈式事務會退回去執行前面各參與者的逆向回滾操作,回滾已提交的參與者,使分佈式事務回到初始狀態。

Saga 正向服務與補償服務也需要業務開發者實現。因此是業務入侵的。

Saga 模式下分佈式事務通常是由事件驅動的,各個參與者之間是異步執行的,Saga 模式是一種長事務解決方案。

Saga 模式使用場景

Saga 模式適用於業務流程長且需要保證事務最終一致性的業務系統,Saga 模式一階段就會提交本地事務,無鎖、長流程情況下可以保證性能。

事務參與者可能是其它公司的服務或者是遺留系統的服務,無法進行改造和提供 TCC 要求的接口,可以使用 Saga 模式。

Saga模式的優勢是:

  • 一階段提交本地數據庫事務,無鎖,高性能;
  • 參與者可以採用事務驅動異步執行,高吞吐;
  • 補償服務即正向服務的“反向”,易於理解,易於實現;

缺點:Saga 模式由於一階段已經提交本地數據庫事務,且沒有進行“預留”動作,所以不能保證隔離性。後續會講到對於缺乏隔離性的應對措施。

與TCC實踐經驗相同的是,Saga 模式中,每個事務參與者的衝正、逆向操作,需要支持:

  • 空補償:逆向操作早於正向操作時;
  • 防懸掛控制:空補償後要拒絕正向操作
  • 冪等

XA模式

XA是X/Open DTP組織(X/Open DTP group)定義的兩階段提交協議,XA被許多數據庫(如Oracle、DB2、SQL Server、MySQL)和中間件等工具(如CICS 和 Tuxedo)本地支持 。
X/Open DTP模型(1994)包括應用程序(AP)、事務管理器(TM)、資源管理器(RM)。

XA接口函數由數據庫廠商提供。XA規範的基礎是兩階段提交協議2PC。
JTA(Java Transaction API) 是Java實現的XA規範的增強版 接口。

在XA模式下,需要有一個[全局]協調器,每一個數據庫事務完成後,進行第一階段預提交,並通知協調器,把結果給協調器。協調器等所有分支事務操作完成、都預提交後,進行第二步;第二步:協調器通知每個數據庫進行逐個commit/rollback。
其中,這個全局協調器就是XA模型中的TM角色,每個分支事務各自的數據庫就是RM。

MySQL 提供的XA實現(https://dev.mysql.com/doc/refman/5.7/en/xa.html )

XA模式下的 開源框架有atomikos,其開發公司也有商業版本。
XA模式缺點:事務粒度大。高併發下,系統可用性低。因此很少使用。

(AT、TCC、Saga、XA)模式分析

四種分佈式事務模式,分別在不同的時間被提出,每種模式都有它的適用場景

  • AT 模式是無侵入的分佈式事務解決方案,適用於不希望對業務進行改造的場景,幾乎0學習成本。
  • TCC 模式是高性能分佈式事務解決方案,適用於核心系統等對性能有很高要求的場景。
  • Saga 模式是長事務解決方案,適用於業務流程長且需要保證事務最終一致性的業務系統,Saga 模式一階段就會提交本地事務,無鎖,長流程情況下可以保證性能,多用於渠道層、集成層業務系統。事務參與者可能是其它公司的服務或者是遺留系統的服務,無法進行改造和提供 TCC 要求的接口,也可以使用 Saga 模式。
  • XA模式是分佈式強一致性的解決方案,但性能低而使用較少。

ref https://juejin.im/post/5d54effe6fb9a06aeb10b646

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章