分佈式事務解決方案

將一個總體模塊拆分爲多個微服務,某些業務場景須要同時操做多個原子服務的數據,分佈式事務就是用來保證多個原子服務數據源一致性的解決方案。算法

分佈式事務產生的緣由?

數據庫分庫分表:因爲單表的數據量巨大須要分庫分表,分庫分表以後,此時一個操做可能涉及訪問多個數據庫,爲了保證數據一致性,就須要用到分佈式事務。 應用SOA化:指的是業務的服務化,將一個總體的系統拆分爲多個子系統,每一個子系統都有本身的數據庫,爲了保證數據一致性,就須要用到分佈式事務。數據庫

常見的分佈式解決方案

1. 兩階段提交/XA(2PC)

兩階段提交的算法思路能夠歸納爲:參與者將操做成功或失敗的結果通知協調者,再由協調者根據全部參與者的反饋狀況決定個參與者是否要提交操做仍是停止操做。網絡

第一階段:請求階段(投票階段)

  1. 協調者向全部的參與者發送事務執行請求,並等待參與者反饋事務執行結果。
  2. 事務參與者收到請求以後,本地執行事務,但不提交。
  3. 參與者將本身事務執行狀況反饋給協調者,同時等待協調者的下一步通知。

第二階段:提交階段(執行階段)

在第一階段協調者的詢盤以後,各個參與者會回覆本身事務的執行狀況,這時候存在三種可能:異步

  1. 全部的參與者回覆可以正常執行事務。分佈式

    • 協調者向各個參與者發送commit通知,請求提交事務。
    • 參與者收到事務提交通知以後,執行commit操做。
    • 參與者向協調者返回事務commit結果信息。
  2. 一個或多個參與者回覆事務執行失敗。微服務

  3. 協調者等待超時。編碼

    • 協調者向各個參與者發送事務rollback通知,請求回滾事務。
    • 參與者收到事務回滾通知以後,執行rollback操做。
    • 參與者向協調者返回事務rollback結果信息。

兩階段提交的缺點

  1. 同步阻塞:執行過程當中,全部參與者的節點都是事務阻塞型的。當參與者佔用公共資源時,其餘第三方節點訪問公共資源不得不處於阻塞狀態。
  2. 單點故障:因爲協調者的重要性,一旦協調者發生故障,參與者會一直阻塞,尤爲時在第二階段,協調者發生故障,那麼全部的參與者都處於鎖定事務資源的狀態中,而沒法繼續完成事務操做。(若是是協調者掛掉,能夠從新選舉一個協調者,可是沒法解決由於協調者宕機致使的參與者處於阻塞狀態的問題)
  3. 數據不一致:在第二階段中,當協調者想參與者發送commit請求以後,發生了局部網絡異常或者在發送commit請求過程當中協調者發生了故障,這會致使只有一部分參與者接收到了commit請求。而在這部分參與者接到commit請求以後就會執行commit操做。可是其餘部分未接收到commit請求的節點則沒法提交事務。因而,整個分佈式系統就出現了數據不一致的現象。

兩階段提交沒法解決的問題

當協調者和參與者同時出現故障時,兩階段提交沒法保證事務的完整性。若是調者在發出commit消息以後宕機,而惟一接收到commit消息的參與者同時也宕機了。那麼即便協調者經過選舉協議產生了新的協調者,這條事務的狀態也是不肯定的,由於沒人知道事務是否已經被提交。spa

2. 三階段提交

第一階段:CanCommit

協調者向參與者發送事務執行請求CanCommit,參與者若是能夠提交就返回YES響應,不然就返回NO響應。中間件

第二階段:PreCommit

協調者根據參與者反饋的結果來決定是否繼續執行事務的PreCommit操做,根據協調者反饋的結果,有如下兩種可能:blog

  1. 假如協調者收到參與者的反饋結果都是YES,那麼就會執行PreCommit操做。
    • 發送預提交請求:協調者向參與者發送PreCommit請求,並進入Prepared階段。
    • 事務預提交:參與者接收到PreCommit請求後,執行事務操做。
    • 響應反饋:事務操做執行成功,則返回ACK響應,而後等待協調者的下一步通知。
  2. 假若有任何一個參與者向協調者發送了NO響應,或者等待超時以後,協調者沒有收到參與者的響應,那麼就中斷事務。
    • 發送中斷請求:協調者向全部參與者發送中斷請求。
    • 中斷事務:參與者收到中斷請求以後(或超時以後,仍未收到協調者的請求),執行事務中斷操做。

第三階段:DoCommit

  1. 執行提交
    • 發送提交請求:協調者收到ACK以後,向全部的參與者發送DoCommit請求。
    • 事務提交:參與者收到DoCommit請求以後,提交事務。
    • 響應反饋:事務提交以後,向協調者發送ACK響應。
    • 完成事務:協調者收到ACK響應以後,完成事務。
  2. 中斷事務 在第二階段,協調者沒有收到參與者發送的ACK響應,那麼就會執行中斷事務。

3. 補償事務(TCC)

TCC是一種比較成熟的分佈式事務解決方案,可用於解決跨庫操做的數據一致性問題,適用於公司內部對一致性、實時性要求較高的業務場景。其中Try、Confirm、Cancel 3個方法均由業務編碼實現。其中Try操做爲第一階段,負責資源的檢查和預留;Confirm操做爲第二階段,執行真正的業務操做;Cancel時執行取消(回滾)操做。 業務實現TCC服務以後,該TCC服務將做爲分佈式事務的其中一個資源,參與到整個分佈式事務中;事務管理器分兩階段協調的TCC服務,第一階段調用全部TCC服務的Try方法,在第二階段執行全部TCC服務的Confirm或者Cancel方法。

實現TCC服務時注意事項

  1. 業務操做分兩階段完成 接入TCC前,業務操做只須要一步就能完成,可是在接入TCC以後,須要考慮如何將其分紅2階段完成,把資源的檢查和預留放在一階段的Try操做中進行,把真正的業務操做的執行放在二階段的Confirm操做中進行。TCC服務要保證第一階段Try操做成功以後,二階段Confirm操做必定能成功。

2. 容許空回滾事務協調器在調用TCC服務的一階段Try操做時,可能會出現由於丟包而致使的網絡超時,此時事務協調器會觸發二階段回滾,調用TCC服務的Cancel操做;TCC服務在未收到Try請求的狀況下收到Cancel請求,這種場景被稱爲空回滾;TCC服務在實現時應當容許空回滾的執行。

3. 防懸掛控制事務協調器在調用TCC服務的一階段Try操做時,可能會出現因網絡擁堵而致使的超時,此時事務協調器會觸發二階段回滾,調用TCC服務的Cancel操做;在此以後,擁堵在網絡上的一階段Try數據包被TCC服務收到,出現了二階段Cancel請求比一階段Try請求先執行的狀況;用戶在實現TCC服務時,應當容許空回滾,可是要拒絕執行空回滾以後到來的一階段Try請求。

4. 冪等控制不管是網絡數據包重傳,仍是異常事務的補償執行,都會致使TCC服務的Try、Confirm或者Cancel操做被重複執行;用戶在實現TCC服務時,須要考慮冪等控制,即Try、Confirm、Cancel 執行一次和執行屢次的業務結果是同樣的。

舉例,假入 Bob 要向 Smith 轉帳,思路大概是: 1. 首先在 Try 階段,要先調用遠程接口把 Smith 和 Bob 的錢給凍結起來。 2. 在 Confirm 階段,執行遠程調用的轉帳的操做,轉帳成功進行解凍。 3. 若是第2步執行成功,那麼轉帳成功,若是第二步執行失敗,則調用遠程凍結接口對應的解凍方法 (Cancel)。

4. 本地消息表

基於本地消息的最終一致性方案的最核心作法就是在執行業務操做的時候,記錄一條消息數據到DB,而且消息數據的記錄與業務數據的記錄必須在同一個事務內完成,這是該方案的前提核心保障。在記錄完成後消息數據後,後面咱們就能夠經過一個定時任務到DB中去輪詢狀態爲待發送的消息,而後將消息投遞給MQ。這個過程當中可能存在消息投遞失敗的可能,此時就依靠 重試機制 來保證,直到成功收到MQ的ACK確認以後,再將消息狀態更新或者消息清除;然後面消息的消費失敗的話,則依賴MQ自己的重試來完成,其最後作到兩邊系統數據的最終一致性。基於本地消息服務的方案雖然能夠作到消息的最終一致性,可是它有一個比較嚴重的弊端,每一個業務系統在使用該方案時,都須要在對應的業務庫建立一張消息表來存儲消息。

5. MQ事務消息

RocketMQ中間件可以支持一種事務消息機制,確保本地操做和發送消息的異步處理達到本地事務的結果一致。

  1. 第一階段,生產者在執行事務以前,首先向MQ發送一個Prepare消息(消息保存在broker中,不會被消息者看到),RocketMQ可以拿到消息的地址。
  2. 第二階段,生產者執行本地事務操做。
  3. 第三階段,確認消息發送,經過第一階段拿到的地址取訪問消息,並修改狀態。

注:若是確認消息發送失敗, RocketMQ會按期掃描消息集羣中的事務消息 ,若是發現了Prepare消息,它會向消息的發送者確認本地事務是否已執行成功,而後再根據業務實現的策略決定時繼續發送仍是回滾(消息生產者須要設置監聽)。

6. 最大努力通知

舉例,訂單支付以後,支付寶向商戶推送支付結果,若是商戶沒有回覆Success,支付寶會重複推送N次支付結果。

相關文章
相關標籤/搜索