分佈式事務解決方案以及常用框架

解決方案

  • 兩階段提交(2PC/XA)
    兩階段提交又稱2PC,2PC是一個非常經典的強一致、中心化的原子提交協議。

    這裏所說的中心化是指協議中有兩類節點:一個是中心化協調者節點(coordinator)和N個參與者節點(partcipant)。

    兩個階段:第一階段:投票階段 和第二階段:提交/執行階段。

    舉:訂單服務A,需要調用支付服務B去支付,支付成功則處理購物訂單爲待發貨狀態,否則就需要將購物訂單處理爲失敗狀態。

    那麼看2PC階段是如何處理的

    第一階段:投票階段在這裏插入圖片描述
    第一階段主要分爲3步:
    1)事務詢問
    協調者 向所有的 參與者 發送事務預處理請求,稱之爲Prepare,並開始等待各 參與者 的響應。
    2)執行本地事務
    各個 參與者 節點執行本地事務操作,但在執行完成後並不會真正提交數據庫本地事務,而是先向 協調者 報告說:“我這邊可以處理了/我這邊不能處理”。.
    3)各參與者向協調者反饋事務詢問的響應
    如果 參與者 成功執行了事務操作,那麼就反饋給協調者 Yes 響應,表示事務可以執行,如果沒有 參與者 成功執行事務,那麼就反饋給協調者 No 響應,表示事務不可以執行。
    第一階段執行完後,會有兩種可能。1、所有都返回Yes. 2、有一個或者多個返回No。

    第二階段:提交/執行階段(成功流程)
    在這裏插入圖片描述
    第二階段主要分爲兩步
    1)所有的參與者反饋給協調者的信息都是Yes,那麼就會執行事務提交。協調者向所有參與者節點發出Commit請求。
    2)事務提交
    參與者收到Commit請求之後,就會正式執行本地事務Commit操作,並在完成提交之後釋放整個事務執行期間佔用的事務資源。

    第二階段:提交/執行階段(異常流程)
    在這裏插入圖片描述
    異常條件:任何一個參與者向協調者反饋了No響應,或者等待超時之後,協調者尚未收到所有參與者的反饋響應。
    異常流程第二階段也分爲兩步
    1)發送回滾請求
    協調者向所有參與者節點發出RoollBack請求.
    2)事務回滾
    參與者 接收到RoollBack請求後,會回滾本地事務。

    2PC缺點
    通過上面的演示,很容易想到2pc所帶來的缺陷
    1)性能問題
    無論是在第一階段的過程中,還是在第二階段,所有的參與者資源和協調者資源都是被鎖住的,只有當所有節點準備完畢,事務 協調者 纔會通知進行全局提交,
    參與者 進行本地事務提交後纔會釋放資源。這樣的過程會比較漫長,對性能影響比較大。
    2)單節點故障
    由於協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去。尤其在第二階段,協調者發生故障,那麼所有的參與者還都處於
    鎖定事務資源的狀態中,而無法繼續完成事務操作。(雖然協調者掛掉,可以重新選舉一個協調者,但是無法解決因爲協調者宕機導致的參與者處於阻塞狀態的問題)

    2PC出現單點問題的三種情況
    (1)協調者正常,參與者宕機
    由於協調者無法收集到所有參與者的反饋,會陷入阻塞情況。
    解決方案:引入超時機制,如果協調者在超過指定的時間還沒有收到參與者的反饋,事務就失敗,向所有節點發送終止事務請求。
    (2)協調者宕機,參與者正常
    無論處於哪個階段,由於協調者宕機,無法發送提交請求,所有處於執行了操作但是未提交狀態的參與者都會陷入阻塞情況.
    解決方案:引入協調者備份,同時協調者需記錄操作日誌.當檢測到協調者宕機一段時間後,協調者備份取代協調者,並讀取操作日誌,向所有參與者詢問狀態。
    (3)協調者和參與者都宕機
    1、發生在第一階段:因爲第一階段,所有參與者都沒有真正執行commit,所以只需重新在剩餘的參與者中重新選出一個協調者,新的協調者在重新執行第一階段和第二階段就可以了。
    2、發生在第二階段並且掛了的參與者在掛掉之前沒有收到協調者的指令。也就是上面的第4步掛了,這是可能協調者還沒有發送第4步就掛了。這種情形下,新的協調者重新執行第一階段和第二階段操作。
    3、發生在第二階段 並且 有部分參與者已經執行完commit操作。就好比這裏訂單服務A和支付服務B都收到協調者 發送的commit信息,開始真正執行本地事務commit,但突發情況,Acommit成功,B確掛了。這個時候目前來講數據是不一致的。雖然這個時候可以再通過手段讓他和協調者通信,再想辦法把數據搞成一致的,但是,這段時間內他的數據狀態已經是不一致的了!2PC 無法解決這個問題。

  • 三階段提交(3PC)
    在這裏插入圖片描述
    三階段提交協議(3PC)主要是爲了解決兩階段提交協議的阻塞問題,2pc存在的問題是當協作者崩潰時,參與者不能做出最後的選擇。因此參與者可能在協作者恢復之前保持阻塞。
    三階段提交(Three-phase commit),是二階段提交(2PC)的改進版本。
    與兩階段提交不同的是,三階段提交有兩個改動點:
    1)引入超時機制。同時在協調者和參與者中都引入超時機制。
    2)在第一階段和第二階段中插入一個準備階段。保證了在最後提交階段之前各參與節點的狀態是一致的。

    也就是說,除了引入超時機制之外,3PC把2PC的準備階段再次一分爲二,這樣三階段提交就有CanCommit、PreCommit、DoCommit三個階段。
    1、CanCommit階段
    之前2PC的一階段是本地事務執行結束後,最後不Commit,等其它服務都執行結束並返回Yes,由協調者發生commit才真正執行commit。而這裏的CanCommit指的是嘗試獲取數據庫鎖 如果可以,就返回Yes。
    在這裏插入圖片描述 這階段主要分爲2步:
    事務詢問協調者向參與者發送CanCommit請求。詢問是否可以執行事務提交操作。然後開始等待參與者的響應。
    響應反饋參與者接到CanCommit請求之後,正常情況下,如果其自身認爲可以順利執行事務,則返回Yes響應,並進入預備狀態。否則反饋No

    2、PreCommit階段
    在階段一中,如果所有的參與者都返回Yes的話,那麼就會進入PreCommit階段進行事務預提交。這裏的PreCommit階段 跟上面的第一階段是差不多的,只不過這裏 協調者和參與者都引入了超時機制 (2PC中只有協調者可以超時,參與者沒有超時機制)。

    3、DoCommit階段
    這裏跟2pc的階段二是差不多的。

    總結
    相比較2PC而言,3PC對於協調者(Coordinator)和參與者(Partcipant)都設置了超時時間,而2PC只有協調者才擁有超時機制。這解決了一個什麼問題呢?

    這個優化點,主要是避免了參與者在長時間無法與協調者節點通訊(協調者掛掉了)的情況下,無法釋放資源的問題,因爲參與者自身擁有超時機制會在超時後,自動進行本地commit從而進行釋放資源。而這種機制也側面降低了整個事務的阻塞時間和範圍。

    另外,通過CanCommit、PreCommit、DoCommit三個階段的設計,相較於2PC而言,多設置了一個緩衝階段保證了在最後提交階段之前各參與節點的狀態是一致的。

    以上就是3PC相對於2PC的一個提高(相對緩解了2PC中的前兩個問題),但是3PC依然沒有完全解決數據不一致的問題。

  • TCC ( Try、Confirm、Cancel )
    TCC三個操作描述:
    1)Try: 檢測、預留資源;
    2)Confirm: 業務系統執行提交;默認Confirm階段是不會出錯的,只要TRY成功,CONFIRM一定成功;
    3)Cancel: 業務取消,預留資源釋放;在這裏插入圖片描述
    TCC又稱補償事務。其核心思想是:“針對每個操作都要註冊一個與其對應的確認和補償(撤銷操作)”。
    它分爲三個操作:
    1、Try階段:主要是對業務系統做檢測及資源預留。
    2、Confirm階段:確認執行業務操作。
    3、Cancel階段:取消執行業務操作。

    TCC對應 Try、Confirm、Cancel 三種操作可以理解成關係型數據庫事務的三種操作:DML、Commit、Rollback。

    在一個跨應用的業務操作中
    Try:Try操作是先把多個應用中的業務資源預留和鎖定住,爲後續的確認打下基礎,類似的,DML操作要鎖定數據庫記錄行,持有數據庫資源。

    Confirm:Confirm操作是在Try操作中涉及的所有應用均成功之後進行確認,使用預留的業務資源,和Commit類似;

    Cancel:Cancel則是當Try操作中涉及的所有應用沒有全部成功,需要將已成功的應用進行取消(即Rollback回滾)。其中Confirm和Cancel操作是一對反向業務操作。

    TCC的具體原理圖如(盜圖):
    在這裏插入圖片描述

    從圖中我們可以明顯看到Confirm和Cancel操作是一對反向業務操作即要try返回成功執行Confirm,要麼try返回失敗執行Cancel操作。

    分佈式事務協調者:分佈式事務協調者管理控制整個業務活動,包括記錄維護TCC全局事務的事務狀態和每個從業務服務的子事務狀態,並在業務活動提交時確認所有的TCC型

    操作的confirm操作,在業務活動取消時調用所有TCC型操作的cancel操作。

    舉例
    A服務轉30塊錢、B服務轉50塊錢,一起到C服務上。

    Try:嘗試執行業務。完成所有業務檢查(一致性):檢查A、B、C的帳戶狀態是否正常,帳戶A的餘額是否不少於30元,帳戶B的餘額是否不少於50元。預留必須業務資源

    (準隔離性):帳戶A的凍結金額增加30元,帳戶B的凍結金額增加50元,這樣就保證不會出現其他併發進程扣減了這兩個帳戶的餘額而導致在後續的真正轉帳操作過程中,帳戶A和B的可用餘額不夠的情況。

    Confirm:確認執行業務。真正執行業務:如果Try階段帳戶A、B、C狀態正常,且帳戶A、B餘額夠用,則執行帳戶A給賬戶C轉賬30元、帳戶B給賬戶C轉賬50元的轉帳操作。 這時已經不需要做任何業務檢查,Try階段已經完成了業務檢查。只使用Try階段預留的業務資源:只需要使用Try階段帳戶A和帳戶B凍結的金額即可。

    Cancel:取消執行業務釋放Try階段預留的業務資源:如果Try階段部分成功,比如帳戶A的餘額夠用,且凍結相應金額成功,帳戶B的餘額不夠而凍結失敗,則需要對帳戶A做Cancel操作,將帳戶A被凍結的金額解凍掉。

    TCC和2PC比較
    2PC是資源層面的分佈式事務,強一致性,在兩階段提交的整個過程中,一直會持有資源的鎖。

    XA事務中的兩階段提交內部過程是對開發者屏蔽的,事務管理器在兩階段提交過程中,從prepare到commit/rollback過程中,資源實際上一直都是被加鎖的。

    如果有其他人需要更新這兩條記錄,那麼就必須等待鎖釋放。

    TCC是業務層面的分佈式事務,最終一致性,不會一直持有資源的鎖。

    我的理解就是當執行try接口的時候,已經把所需的資源給預扣了,比如上面舉例的A服務已經預扣30元,B服務已經預扣50元,它是由try接口實現,這樣就保證不會

    出現其他併發進程扣減了這兩個帳戶的餘額而導致在後續的真正轉帳操作過程中,帳戶A和B的可用餘額不夠的情況,同時保證不會一直鎖住整個資源。(核心點應該就在這)

    TCC中的兩階段提交併沒有對開發者完全屏蔽,也就是說從代碼層面,開發者是可以感受到兩階段提交的存在。

    1、try過程的本地事務,是保證資源預留的業務邏輯的正確性。

    2、confirm/cancel執行的本地事務邏輯確認/取消預留資源,以保證最終一致性,也就是所謂的補償型事務。

    由於是多個獨立的本地事務,因此不會對資源一直加鎖。

    TCC 實質上是應用層的2PC ,好比把 XA 兩階段提交那種在數據資源層做的事務管理工作提到了數據應用層。

    2PC是資源層面的分佈式事務,是強一致性,在兩階段提交的整個過程中,一直會持有資源的鎖。

    TCC是業務層面的分佈式事務,最終一致性,不會一直持有資源的鎖。

    TCC相比較於2PC來講性能會好很多,但是因爲同時需要改造try、confirm、canel3個接口,開發成本高。

    注意:還有一點需要注意的是Confirm和Cancel操作可能被重複調用,故要求Confirm和Cancel兩個接口必須是冪等。

  • SAGA
    詳見1:https://www.jianshu.com/p/cf0cdc03f74c
    詳見2:https://zhuanlan.zhihu.com/p/95852045
    詳見3:https://blog.csdn.net/lsblsb/article/details/89456255

常用框架推薦

2PC/XA 實現方案 Spring+JTA
微服務中單個服務跨數據庫是不規範的,每個服務只能操作自己的數據庫。所以JTA方案不常用。

  • 阿里巴巴fescar

  • 華爲servicecomb-pack

  • Hmily

  • byteTCC

  • tcc-transaction

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