一、什么是分布式事務
首先我們知道本地事務是指事務方法中的操作只依賴本地數據庫,可保證事務的ACID特性。而在分布式系統中,一個應用系統被拆分為多個可獨立部署的微服務,在一個微服務的事務方法中,除了依賴本地數據庫外,還可能會調用一個或多個遠程服務操作遠程數據庫,這種就叫做分布式事務。
在分布式事務中,如果由于網絡波動導致遠程調用執行成功了,但是沒有及時返回結果,導致事務回滾,本地數據庫回滾了,但是遠程數據庫已經執行成功持久化了,這就出現了不一致的情況。
二、Base理論
在CAP理論中的一致性強調的是強一致性。
BASE 是 Basically Available(基本可用)、Soft state(軟狀態)和 Eventually consistent (最終一致性)三個短語的縮
寫。BASE理論是對CAP中AP的一個擴展,通過犧牲強一致性來獲得可用性,當出現故障允許部分不可用但要保證
核心功能可用,允許數據在一段時間內是不一致的,但最終達到一致狀態。滿足BASE理論的事務,我們稱之為“柔
性事務”。
- 基本可用:分布式系統在出現故障時,允許損失部分可用功能,保證核心功能可用。如,電商網站交易付款出
現問題了,商品依然可以正常瀏覽。 - 軟狀態:由于不要求強一致性,所以BASE允許系統中存在中間狀態(也叫軟狀態),這個狀態不影響系統可用
性,如訂單的"支付中"、“數據同步中”等狀態,待數據最終一致后狀態改為“成功”狀態。 - 最終一致性:最終一致是指經過一段時間后,所有節點數據都將會達到一致。如訂單的"支付中"狀態,最終會變
為“支付成功”或者"支付失敗",使訂單狀態與實際交易結果達成一致,但需要一定時間的延遲、等待。
前面已經學習了分布式事務的基礎理論,以理論為基礎,針對不同的分布式場景業界常見的解決方案有2PC、可靠消息最終一致性、最大努力通知這幾種。
三、2PC —— 兩階段提交
-
P 準備階段:事務管理器給每個參與者發送Prepare消息,每個數據庫參與者在本地執行事務,并寫本地的Undo/Redo日志,但是先不提交事務,然后給事務管理器回復一個OK,表示準備好了。(Undo日志是記錄修改前的數據,用于數據庫回滾,Redo日志是記錄修改后的數據,用于提交事務后寫入數據文件)
-
C 提交階段:如果所有參與者都準備好了,事務管理器就會發送一個commit消息,所有參與者再執行事務提交。如果事務管理器收到了參與者的執行失敗或者超時消息時,直接給每個參與者發送回滾(Rollback)消息,所有參與者就都執行回滾。
成功情況:
失敗情況:
1. XA模式 —— 強一致性
XA模式流程就如上所說,第一階段參與者只執行不提交事務,第二階段收到Commit信號后再進行提交。這種模式可以基于數據庫的XA協議來實現。也可以基于一些第三方框架實現,比如Seata,Seata是由阿里中間件團隊做的一個是開源的分布式事務框架。它是工作在應用層,不需要數據庫支持XA協議,因此兼容性更好。它由事務協調器、事務管理器、資源管理器三部分組成:
- Transaction Coordinator (TC): 事務協調器,它是獨立的中間件,需要獨立部署運行,它維護全局事務的運行狀態,接收TM指令發起全局事務的提交與回滾,負責與RM通信協調各各分支事務的提交或回滾。
- Transaction Manager(TM): 事務管理器,TM需要嵌入應用程序中工作,它負責開啟一個全局事務,并最終向TC發起全局提交或全局回滾的指令。
- Resource Manager (RM): 資源管理器,控制分支事務,負責分支注冊、狀態匯報,并接收事務協調器TC的指令,驅動分支(本地)事務的提交和回滾。
Seata的XA模式流程如下:
-
一階段:TM開啟全局事務注冊到TC 、TM調用分支RM、RM將分支事務注冊到TC、RM執行SQL(但不提交!)、RM將執行狀態報告給TC
-
二階段:TM通知提交全局事務、TC檢查各分支事務狀態,如果都成功,則通知RM提交。如果失敗,則通知RM回滾。
XA模式優缺點:XA模式保證了強一致性,但是資源鎖需要等到兩個階段結束才釋放,性能較差。
2. AT模式 —— 弱一致性
AT模式是Seata中的默認模式
-
一階段:TM開啟全局事務注冊到TC、TM調用分支RM、RM將分支事務注冊到TC、RM記錄undolog日志、RM提交事務、TCC記錄各分支狀態
-
二階段:TM通知提交全局事務、TC檢查各分支事務狀態,都成功則刪除undolog日志,有失敗則通知所有RM根據undolog日志執行反向補償操作回滾。
AT模式優缺點:AT模式在第一階段就提交了事務釋放了資源鎖,性能較高。但是由于提前提交了事務,如果在回滾之前,有其它事務修改了數據,那么再根據undolog日志回滾后就會覆蓋掉了這個修改,出現臟寫問題。需要引入全局鎖解決。
3. TCC模式 —— 弱一致性
TCC模式分為Try、Confirm和Cancel三個操作
- 一階段:通過Try操作判斷是否有可用數據,有則鎖住需要的資源。
- 二階段:如果全部try成功則執行confirm操作,完成資源的操作業務,且try成功confirm一定要成功,無論是通過重試還是人工介入。如果有try失敗的,則所有try成功的節點執行Cancel操作釋放預留資源。
TCC模式優缺點: try、confirm、canel需要人工手寫,而且需要考慮冪等性、空回滾、懸掛判斷,較為復雜、性能最好,但成本太高。
冪等性: try、confirm、canel這三個接口,要保證重試操作具有冪等性
空回滾:沒有執行try操作的節點回滾時執行了cancel。解決方案:用一張“分支事務記錄”記錄是否執行過try操作,執行cancel時要進行查詢,執行過try操作才需要回滾。
懸掛:try操作由于網絡波動超時了,導致觸發回滾cancel操作,在執行完cancel后try操作請求到達了,這種先cancel再try的現象就稱為懸掛。解決方案:在執行一階段事務時判斷在該全局事務下,“分支事務記錄”表中是否已經有二階段事務記錄,如果有則不執行Try。
參考:
- https://blog.csdn.net/m0_58600248/article/details/126271252
- https://blog.csdn.net/O_Dentist/article/details/130966668
四、可靠消息最終一致性
可靠消息最終一致性方案是通過消息中間件完成的,指當事務發起方執行完成本地事務后并發出一條消息,事務參與方(消息消費者)一定能
夠接收消息并處理事務成功,使得所有事務參與方最終事務達到一致。
要達成這種效果需要解決以下問題:
- 原子性:本地事務和消息發送必須同時成功或者同時失敗,具有原子性。
- 可靠性:事務參與方必須能夠在消息隊列接收到消息,接收失敗可以重復接收。
- 冪等性: 事務參與方不能重復消費消息。
1. 本地消息表
本地數據庫增加一個消息表,將本地事務操作和添加消息記錄放在同一個事務中,然后后臺定時任務去循環掃描這個消息表,檢測到未發送的消息時就交給MQ發送,消費端收到這個消息后通過MQ回復ACK確認,發送端收到MQ反饋后再刪除對應的消息記錄,消費端要對收到的消息進行冪等性檢查避免重復消費(發送端可能會重復發送),非重復消費則消費執行事務。
2. RocketMQ事務消息方案
- 在執行事務前會先發消息給MQ服務端,但是這個消息是不可消費狀態。
- 然后發送方再執行本地事務,執行成功/失敗后再給MQ服務端發送一個commit或者rollback事務確認消息,如果執行成功了MQ服務端再把消息投遞給訂閱方,如果執行失敗了則直接丟棄原來的消息。
- 如果確認消息在中間丟失了,MQ服務端沒有收到 則會定期回查事務的狀態。
在RocketMQ 4.3后實現了完整的事務消息,實際上其實是對本地消息表的一個封裝,將本地消息表移動到了MQ 內部,解決 Producer 端的消息發送與本地事務執行的原子性問題。
五、最大努力通知
發起通知方通過一定的機制最大努力將業務處理結果通知到接收方,比如重復通知,并且發送方要提供消息校對接口,若盡最大努力仍沒有通知到,此時可由接收方主動向通知方查詢消息信息來滿足需求。
1. MQ最大努力通知實現方案
- 發送方通過MQ將消息發送出去。接收方收到后會回復一個ack,發送方收到ack則通知成功了。
- 若發送方沒有收到ack,則進行重傳,直到超過一定次數。
- 接收方可主動通過發送方提供的接口進行消息校對,獲取需要的消息。
參考:https://www.bilibili.com/video/BV1Q4411y7ip