淺談分佈式事務的解決方案

一、分佈式事務

分佈式事務就是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位於不同的分佈式系統的不同節點之上。以上是百度百科的解釋,簡單的說,就是一次大的操作由不同的小操作組成,這些小的操作分佈在不同的服務器上,且屬於不同的應用,分佈式事務需要保證這些小操作要麼全部成功,要麼全部失敗。本質上來說,分佈式事務就是爲了保證不同數據庫的數據一致性。

畫外音:提起分佈式系統,我們不得不想起CAP理論。這裏不展開討論,可以參考前篇文章分佈式系統理解之CAP理論的發展

以商品流水賬單爲例,我們拆分爲商品購買系統,訂單系統,支付系統。

  1. 用戶看中一件商品,點擊購買。
  2. 商品購買系統響應用戶的點擊,向訂單系統插入一條訂單信息。
  3. 跳轉到支付系統完成支付。

在用戶整個購買商品的過程中,我們需要保證事件1,2,3在沒有異常的情況下全部執行成功,一旦某個系統拋出異常,都需要回滾。

那麼,如何保證各個子系統的操作具有一致性呢?這就是我們下面提到的分佈式事務的解決方案。

在這裏,文章中沒有提到分佈式一致性協議,下面簡單列舉一下,有興趣的讀者可以參考其他詳細資料:

  1. 兩階段提交協議
  2. 三階段提交協議
  3. Paxos協議
  4. Raft協議

二、分佈式事務的解決方案

1.兩階段提交方案/XA方案

該方案基於兩階段提交協議,因此也叫做兩階段提交方案。在該分佈式系統中,其中 需要一個系統擔任協調器的角色,其他系統擔任參與者的角色。主要分爲Commit-request階段和Commit階段

  • 請求階段:首先協調器會向所有的參與者發送準備提交或者取消提交的請求,然後會收集參與者的決策。
  • 提交階段:協調者會收集所有參與者的決策信息,當且僅當所有的參與者向協調器發送確認消息時協調器纔會提交請求,否則執行回滾或者取消請求。

在這裏插入圖片描述
(上圖摘自網上)

該方案的缺陷:

  1. 同步阻塞:所有的參與者都是事務同步阻塞型的。當參與者佔有公共資源時,其他第三方節點訪問公共資源不得不處於阻塞狀態。
  2. 單點故障:一旦協調器發生故障,系統不可用。
  3. 數據不一致:當協調器發送commit之後,有的參與者收到commit消息,事務執行成功,有的沒有收到,處於阻塞狀態,這段時間會產生數據不一致性。
  4. 不確定性:當協調器發送commit之後,並且此時只有一個參與者收到了commit,那麼當該參與者與協調器同時宕機之後,重新選舉的協調器無法確定該條消息是否提交成功。

XA方案的實現方式可以使用Spring+JTA來實現,可以參考文章:Springboot+atomikos+jta實現分佈式事務統一管理

2.TCC方案

TCC方案分爲Try Confirm Cancel三個階段,屬於補償性分佈式事務。

  • Try:嘗試待執行的業務
    這個過程並未執行業務,只是完成所有業務的一致性檢查,並預留好執行所需的全部資源
  • Confirm:執行業務
    這個過程真正開始執行業務,由於Try階段已經完成了一致性檢查,因此本過程直接執行,而不做任何檢查。並且在執行的過程中,會使用到Try階段預留的業務資源。
  • Cancel:取消執行的業務
    若業務執行失敗,則進入Cancel階段,它會釋放所有佔用的業務資源,並回滾Confirm階段執行的操作。

以一個電商系統用戶購買商品的流水線爲例。

  • Try階段:
    在這裏插入圖片描述
    在Try階段成功之後進入Confirm階段,如有任何異常,進入Cancel階段。

  • Confirm階段
    在這裏插入圖片描述

  • Cancel階段
    假設庫存扣減失敗,此時需要回滾取消事務。
    在這裏插入圖片描述

TCC方案適用於一致性要求極高的系統中,比如金錢交易相關的系統中,不過可以看出,其基於補償的原理,因此,需要編寫大量的補償事務的代碼,比較冗餘。不過現有開源的TCC框架,比如TCC-transaction。

3. 本地消息表

本地消息表分佈式事務解決方案是國外的eBay提出的一套方案。
在這裏插入圖片描述
需要注意的是,該方案中,在A系統中,我們首先寫入業務表,然後寫入消息表,然後將消息發送到MQ中,在B系統中需要先寫入消息表,這是爲了保證消息重複消費,爲了保證消息消費的冪等性,我們可以使用數據的唯一鍵來約束。

當B系統執行成功之後,需要通知A系統執行成功,此時可以使用一個監聽器,如Zookeeper,ZK監聽到執行成功更新A系統成功。然後開始發送下一條消息。

A系統中需要有一個後臺線程,不斷的去判斷A系統的狀態爲待確認的消息,設置超時機制,如果超時,重新發送到MQ中。直到執行成功。

可以看出,本地消息表方案需要寫入消息表中,如果在高併發的場景下會進行大量的磁盤IO,因此該方案不適用於高併發場景。

4.可靠消息最終一致性方案

該方案基於本地消息表進行優化,不使用本地消息表,而是基於MQ,比如阿里的RocketMQ就支持消息事務。

在這裏插入圖片描述
在該方案中,首先A系統需要向MQ中發送prepare消息,然後執行A系統的業務,寫入數據庫成功之後向MQ發送confirm消息,當消息爲confirm狀態時,B系統就可以消費到消息,消費成功之後返回ACK確認消息給MQ。

需要注意的是。需要保證B系統消費消息的冪等性,可以藉助第三方系統。比如在redis中設置標識,標明已經消費過該消息,或者藉助ZK基於分佈式鎖的原理,創建節點,重複消費消息,創建失敗。

5.最大努力通知方案

最大努力通知型( Best-effort delivery)是最簡單的一種柔性事務,適用於一些最終一致性時間敏感度低的業務,且被動方處理結果 不影響主動方的處理結果。典型的使用場景:如銀行通知、商戶通知等。
在這裏插入圖片描述
在該系統中,A系統執行完本地事務,向MQ發送消息,最大努力通知服務消費消息,比如消息服務,然後調用B系統的接口,執行B系統的本地事務,如果B系統執行成功則OK,否則不斷重試,重複多次之後還是失敗的話就放棄執行。

參考文章:
http://www.tianshouzhi.com/api/tutorials/distributed_transaction/387
https://blog.csdn.net/u010425776/article/details/79516298

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