七種常見分布式事務詳解(2PC、3PC、TCC、Saga、本地事務表、MQ事務消息、最大努力通知)


分布式事務:在分布式系統中一次操作需要由多個服務協同完成,這種由不同的服務之間通過網絡協同完成的事務稱為分布式事務

一、2PC:

        2PC,兩階段提交,將事務的提交過程分為資源准備和資源提交兩個階段,並且由事務協調者來協調所有事務參與者,如果准備階段所有事務參與者都預留資源成功,則進行第二階段的資源提交,否則事務協調者回滾資源。

1、第一階段:准備階段

由事務協調者詢問通知各個事務參與者,是否准備好了執行事務,具體流程圖如下:

 

  • ① 協調者向所有參與者發送事務內容,詢問是否可以提交事務,並等待答復
  • ② 各參與者執行本地事務操作,將 undo 和 redo 信息記入事務日志中(但不提交事務)
  • ③ 如參與者執行成功,給協調者反饋同意,否則反饋中止,表示事務不可以執行

2、第二階段:提交階段

        協調者收到各個參與者的准備消息后,根據反饋情況通知各個參與者commit提交或者rollback回滾

(1)事務提交:

        當第一階段所有參與者都反饋同意時,協調者發起正式提交事務的請求,當所有參與者都回復同意時,則意味着完成事務,具體流程如下:

  • ① 協調者節點向所有參與者節點發出正式提交的 commit 請求。
  • ② 收到協調者的 commit 請求后,參與者正式執行事務提交操作,並釋放在整個事務期間內占用的資源。
  • ③ 參與者完成事務提交后,向協調者節點發送ACK消息。
  • ④ 協調者節點收到所有參與者節點反饋的ACK消息后,完成事務。

所以,正常提交時,事務的完整流程圖如下:

 (2)事務回滾:

如果任意一個參與者節點在第一階段返回的消息為中止,或者協調者節點在第一階段的詢問超時之前無法獲取所有參與者節點的響應消息時,那么這個事務將會被回滾,具體流程如下:

  • ① 協調者向所有參與者發出 rollback 回滾操作的請求
  • ② 參與者利用階段一寫入的undo信息執行回滾,並釋放在整個事務期間內占用的資源
  • ③ 參與者在完成事務回滾之后,向協調者發送回滾完成的ACK消息
  • ④ 協調者收到所有參與者反饋的ACK消息后,取消事務

所以,事務回滾時,完整流程圖如下:

 3、2PC的缺點:

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

(1)性能問題:執行過程中,所有參與節點都是事務阻塞性的,當參與者占有公共資源時,其他第三方節點訪問公共資源就不得不處於阻塞狀態,為了數據的一致性而犧牲了可用性,對性能影響較大,不適合高並發高性能場景

(2)可靠性問題:2PC非常依賴協調者,當協調者發生故障時,尤其是第二階段,那么所有的參與者就會都處於鎖定事務資源的狀態中,而無法繼續完成事務操作(如果是協調者掛掉,可以重新選舉一個協調者,但是無法解決因為協調者宕機導致的參與者處於阻塞狀態的問題)

(3)數據一致性問題:在階段二中,當協調者向參與者發送commit請求之后,發生了局部網絡異常或者在發送commit請求過程中協調者發生了故障,這回導致只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求之后就會執行commit操作。但是其他部分未接到commit請求的機器則無法執行事務提交。於是整個分布式系統便出現了數據部一致性的現象。

(4)二階段無法解決的問題:協調者在發出 commit 消息之后宕機,而唯一接收到這條消息的參與者同時也宕機了,那么即使協調者通過選舉協議產生了新的協調者,這條事務的狀態也是不確定的,沒人知道事務是否被已經提交。

 

二、3PC:

        3PC,三階段提交協議,是二階段提交協議的改進版本,三階段提交有兩個改動點:

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

        所以3PC會分為3個階段,CanCommit 准備階段、PreCommit 預提交階段、DoCommit 提交階段,處理流程如下:

 1、階段一:CanCommit 准備階段

        協調者向參與者發送 canCommit 請求,參與者如果可以提交就返回Yes響應,否則返回No響應,具體流程如下:

  • (1)事務詢問:協調者向所有參與者發出包含事務內容的 canCommit 請求,詢問是否可以提交事務,並等待所有參與者答復。
  • (2)響應反饋:參與者收到 canCommit 請求后,如果認為可以執行事務操作,則反饋 yes 並進入預備狀態,否則反饋 no。

2、階段二:PreCommit 階段

        協調者根據參與者的反應情況來決定是否可以進行事務的 PreCommit 操作。根據響應情況,有以下兩種可能:

(1)執行事務:

假如所有參與者均反饋 yes,協調者預執行事務,具體如下:

  • ① 發送預提交請求:協調者向參與者發送 PreCommit 請求,並進入准備階段
  • ② 事務預提交 :參與者接收到 PreCommit 請求后,會執行本地事務操作,並將 undo 和 redo 信息記錄到事務日志中(但不提交事務)
  • ③ 響應反饋 :如果參與者成功的執行了事務操作,則返回ACK響應,同時開始等待最終指令。

 (2)中斷事務:

假如有任何一個參與者向協調者發送了No響應,或者等待超時之后,協調者都沒有接到參與者的響應,那么就執行事務的中斷,流程如下:

  • ① 發送中斷請求 :協調者向所有參與者發送 abort 請求。
  • ② 中斷事務 :參與者收到來自協調者的 abort 請求之后(或超時之后,仍未收到協調者的請求),執行事務的中斷。

3、階段三:doCommit階段

該階段進行真正的事務提交,也可以分為以下兩種情況:

(1)提交事務:

  • ① 發送提交請求:協調接收到所有參與者發送的ACK響應,那么他將從預提交狀態進入到提交狀態,並向所有參與者發送 doCommit 請求
  • ② 本地事務提交:參與者接收到doCommit請求之后,執行正式的事務提交,並在完成事務提交之后釋放所有事務資源
  • ③ 響應反饋:事務提交完之后,向協調者發送ack響應。
  • ④ 完成事務:協調者接收到所有參與者的ack響應之后,完成事務。

(2)中斷事務:任何一個參與者反饋 no,或者等待超時后協調者尚無法收到所有參與者的反饋,即中斷事務

  • ① 發送中斷請求:如果協調者處於工作狀態,向所有參與者發出 abort 請求
  • ② 事務回滾:參與者接收到abort請求之后,利用其在階段二記錄的undo信息來執行事務的回滾操作,並在完成回滾之后釋放所有的事務資源。
  • ③ 反饋結果:參與者完成事務回滾之后,向協調者反饋ACK消息
  • ④ 中斷事務:協調者接收到參與者反饋的ACK消息之后,執行事務的中斷。

        進入doCommit階段后,無論協調者出現問題,或者協調者與參與者之間的網絡出現問題,都會導致參與者無法接收到協調者發出的 doCommit 請求或 abort 請求。此時,參與者都會在等待超時之后,繼續執行事務提交。這其實基於概率來決定的,當進入第三階段時,說明第一階段收到所有參與者的CanCommit響應都是Yes,意味着大家都同意修改了,並且第二階段所有的參與者對協調者的PreCommit請求也都是同意的。所以,一句話概括就是,當進入第三階段時,由於網絡超時等原因,雖然參與者沒有收到commit或者abort響應,但是他有理由相信:成功提交的幾率很大。

4、3PC的優缺點:

        與2PC相比,3PC降低了阻塞范圍,並且在等待超時后,協調者或參與者會中斷事務,避免了協調者單點問題,階段三中協調者出現問題時,參與者會繼續提交事務。

        數據不一致問題依然存在,當在參與者收到 preCommit 請求后等待 doCommit 指令時,此時如果協調者請求中斷事務,而協調者因為網絡問題無法與參與者正常通信,會導致參與者繼續提交事務,造成數據不一致。

2PC和3PC都無法保證數據絕對的一致性,一般為了預防這種問題,可以添加一個報警,比如監控到事務異常的時候,通過腳本自動補償差異的信息。

 

三、TCC:

1、什么是TCC:

        TCC(Try Confirm Cancel)是應用層的兩階段提交,所以對代碼的侵入性強,其核心思想是:針對每個操作,都要實現對應的確認和補償操作,也就是業務邏輯的每個分支都需要實現 try、confirm、cancel 三個操作,第一階段由業務代碼編排來調用Try接口進行資源預留,當所有參與者的 Try 接口都成功了,事務協調者提交事務,並調用參與者的 confirm 接口真正提交業務操作,否則調用每個參與者的 cancel 接口回滾事務,並且由於 confirm 或者 cancel 有可能會重試,因此對應的部分需要支持冪等。

2、TCC的執行流程:

        TCC的執行流程可以分為兩個階段,分別如下:

(1)第一階段:Try,業務系統做檢測並預留資源 (加鎖,鎖住資源),比如常見的下單,在try階段,我們不是真正的減庫存,而是把下單的庫存給鎖定住。

(2)第二階段:根據第一階段的結果決定是執行confirm還是cancel

  • Confirm:執行真正的業務(執行業務,釋放鎖)
  • Cancle:是對Try階段預留資源的釋放(出問題,釋放鎖)

 3、TCC如何保證最終一致性:

  • TCC 事務機制以 Try 為中心的,Confirm 確認操作和 Cancel 取消操作都是圍繞 Try 而展開。因此,Try 階段中的操作,其保障性是最好的,即使失敗,仍然有 Cancel 取消操作可以將其執行結果撤銷。
  • Try階段執行成功並開始執行 Confirm 階段時,默認 Confirm 階段是不會出錯的,也就是說只要 Try 成功,Confirm 一定成功(TCC設計之初的定義)
  • Confirm 與 Cancel 如果失敗,由TCC框架進行重試補償
  • 存在極低概率在CC環節徹底失敗,則需要定時任務或人工介入

4、TCC的注意事項:

(1)允許空回滾:

        空回滾出現的原因是 Try 超時或者丟包,導致 TCC 分布式事務二階段的 回滾,觸發 Cancel 操作,此時事務參與者未收到Try,但是卻收到了Cancel 請求,如下圖所示:

 

        所以 cancel 接口在實現時需要允許空回滾,也就是 Cancel 執行時如果發現沒有對應的事務 xid 或主鍵時,需要返回回滾成功,讓事務服務管理器認為已回滾。

(2)防懸掛控制:

        懸掛指的是二階段的 Cancel 比 一階段的Try 操作先執行,出現該問題的原因是 Try 由於網絡擁堵而超時,導致事務管理器生成回滾,觸發 Cancel 接口,但之后擁堵在網絡的 Try 操作又被資源管理器收到了,但是 Cancel 比 Try 先到。但按照前面允許空回滾的邏輯,回滾會返回成功,事務管理器認為事務已回滾成功,所以此時應該拒絕執行空回滾之后到來的 Try 操作,否則會產生數據不一致。因此我們可以在 Cancel 空回滾返回成功之前,先記錄該條事務 xid 或業務主鍵,標識這條記錄已經回滾過,Try 接口執行前先檢查這條事務xid或業務主鍵是否已經標記為回滾成功,如果是則不執行 Try 的業務操作。

 (3)冪等控制:

        由於網絡原因或者重試操作都有可能導致 Try - Confirm - Cancel 3個操作的重復執行,所以使用 TCC 時需要注意這三個操作的冪等控制,通常我們可以使用事務 xid 或業務主鍵判重來控制。

5、TCC方案的優缺點:

(1)TCC 事務機制相比於上面介紹的 XA 事務機制,有以下優點:

  • 性能提升:具體業務來實現,控制資源鎖的粒度變小,不會鎖定整個資源。
  • 數據最終一致性:基於 Confirm 和 Cancel 的冪等性,保證事務最終完成確認或者取消,保證數據的一致性。
  • 可靠性:解決了 XA 協議的協調者單點故障問題,由主業務方發起並控制整個業務活動,業務活動管理器也變成多點,引入集群。

(2)缺點:TCC 的 Try、Confirm 和 Cancel 操作功能要按具體業務來實現,業務耦合度較高,提高了開發成本。

 

四、Saga事務:

1、什么是Saga事務:

        Saga 事務核心思想是將長事務拆分為多個本地短事務並依次正常提交,如果所有短事務均執行成功,那么分布式事務提交;如果出現某個參與者執行本地事務失敗,則由 Saga 事務協調器協調根據相反順序調用補償操作,回滾已提交的參與者,使分布式事務回到最初始的狀態。Saga 事務基本協議如下:

  • (1)每個 Saga 事務由一系列冪等的有序子事務(sub-transaction) Ti 組成。
  • (2)每個 Ti 都有對應的冪等補償動作 Ci,補償動作用於撤銷 Ti 造成的結果。

        與TCC事務補償機制相比,TCC有一個預留(Try)動作,相當於先報存一個草稿,然后才提交;Saga事務沒有預留動作,直接提交。

2、Saga的恢復策略:

對於事務異常,Saga提供了兩種恢復策略,分別如下:

(1)向后恢復(backward recovery):

當執行事務失敗時,補償所有已完成的事務,是“一退到底”的方式,這種做法的效果是撤銷掉之前所有成功的子事務,使得整個 Saga 的執行結果撤銷。如下圖:

         從上圖可知事務執行到了支付事務T3,但是失敗了,因此事務回滾需要從C3,C2,C1依次進行回滾補償,對應的執行順序為:T1,T2,T3,C3,C2,C1。

(2)向前恢復(forward recovery):

        對於執行不通過的事務,會嘗試重試事務,這里有一個假設就是每個子事務最終都會成功,這種方式適用於必須要成功的場景,事務失敗了重試,不需要補償。流程如下圖:

 3、Saga事務的實現方式:

Saga事務有兩種不同的實現方式,分別如下:

  • 命令協調(Order Orchestrator)
  • 事件編排(Event Choreographyo)

(1)命令協調:

        中央協調器(Orchestrator,簡稱 OSO)以命令/回復的方式與每項服務進行通信,全權負責告訴每個參與者該做什么以及什么時候該做什么。整體流程如下圖:

  • ① 事務發起方的主業務邏輯請求 OSO 服務開啟訂單事務
  • ② OSO 向庫存服務請求扣減庫存,庫存服務回復處理結果。
  • ③ OSO 向訂單服務請求創建訂單,訂單服務回復創建結果。
  • ④ OSO 向支付服務請求支付,支付服務回復處理結果。
  • ⑤ 主業務邏輯接收並處理 OSO 事務處理結果回復。

        中央協調器 OSO 必須事先知道執行整個事務所需的流程,如果有任何失敗,它還負責通過向每個參與者發送命令來撤銷之前的操作來協調分布式的回滾,基於中央協調器協調一切時,回滾要容易得多,因為協調器默認是執行正向流程,回滾時只要執行反向流程即可。

(2)事件編排:

        命令協調方式基於中央協調器實現,所以有單點風險,但是事件編排方式沒有中央協調器。事件編排的實現方式中,每個服務產生自己的時間並監聽其他服務的事件來決定是否應采取行動。

        在事件編排方法中,第一個服務執行一個事務,然后發布一個事件,該事件被一個或多個服務進行監聽,這些服務再執行本地事務並發布(或不發布)新的事件。當最后一個服務執行本地事務並且不發布任何事件時,意味着分布式事務結束,或者它發布的事件沒有被任何 Saga 參與者聽到都意味着事務結束。

  • ① 事務發起方的主業務邏輯發布開始訂單事件。
  • ② 庫存服務監聽開始訂單事件,扣減庫存,並發布庫存已扣減事件。
  • ③ 訂單服務監聽庫存已扣減事件,創建訂單,並發布訂單已創建事件。
  • ④ 支付服務監聽訂單已創建事件,進行支付,並發布訂單已支付事件。
  • ⑤ 主業務邏輯監聽訂單已支付事件並處理。

        如果事務涉及 2 至 4 個步驟,則非常合適使用事件編排方式,它是實現 Saga 模式的自然方式,它很簡單,容易理解,不需要太多的代碼來構建。

4、Saga事務的優缺點:

(1)命令協調設計的優缺點:

① 優點:

  • 服務之間關系簡單,避免服務間循環依賴,因為 Saga 協調器會調用 Saga 參與者,但參與者不會調用協調器。
  • 程序開發簡單,只需要執行命令/回復(其實回復消息也是一種事件消息),降低參與者的復雜性。
  • 易維護擴展,在添加新步驟時,事務復雜性保持線性,回滾更容易管理,更容易實施和測試。

② 缺點:

  • 中央協調器處理邏輯容易變得龐大復雜,導致難以維護。
  • 存在協調器單點故障風險。

(2)事件編排設計的優缺點:

① 優點:

  • 避免中央協調器單點故障風險。
  • 當涉及的步驟較少服務開發簡單,容易實現。

② 缺點:

  • 服務之間存在循環依賴的風險。
  • 當涉及的步驟較多,服務間關系混亂,難以追蹤調測。

        由於 Saga 模型沒有 Prepare 階段,因此事務間不能保證隔離性。當多個 Saga 事務操作同一資源時,就會產生更新丟失、臟數據讀取等問題,這時需要在業務層控制並發,例如:在應用層面加鎖,或者應用層面預先凍結資源。

 

五、本地消息表:

1、什么是本地消息表:

        本地消息表的核心思路就是將分布式事務拆分成本地事務進行處理,在該方案中主要有兩種角色:事務主動方和事務被動方。事務主動發起方需要額外新建事務消息表,並在本地事務中完成業務處理和記錄事務消息,並輪詢事務消息表的數據發送事務消息,事務被動方基於消息中間件消費事務消息表中的事務。

        這樣可以避免以下兩種情況導致的數據不一致性:

  • 業務處理成功、事務消息發送失敗
  • 業務處理失敗、事務消息發送成功

2、本地消息表的執行流程:

  • ① 事務主動方在同一個本地事務中處理業務和寫消息表操作
  • ② 事務主動方通過消息中間件,通知事務被動方處理事務消息。消息中間件可以基於 Kafka、RocketMQ 消息隊列,事務主動方主動寫消息到消息隊列,事務消費方消費並處理消息隊列中的消息。
  • ③ 事務被動方通過消息中間件,通知事務主動方事務已處理的消息。
  • ④ 事務主動方接收中間件的消息,更新消息表的狀態為已處理。

一些必要的容錯處理如下:

  • 當①處理出錯,由於還在事務主動方的本地事務中,直接回滾即可
  • 當②、③處理出錯,由於事務主動方本地保存了消息,只需要輪詢消息重新通過消息中間件發送,通知事務被動方重新讀取消息處理業務即可。
  • 如果是業務上處理失敗,事務被動方可以發消息給事務主動方回滾事務
  • 如果事務被動方已經消費了消息,事務主動方需要回滾事務的話,需要發消息通知事務主動方進行回滾事務。

3、本地消息表的優缺點:

(1)優點:

  • 從應用設計開發的角度實現了消息數據的可靠性,消息數據的可靠性不依賴於消息中間件,弱化了對 MQ 中間件特性的依賴。
  • 方案輕量,容易實現。

(2)缺點:

  • 與具體的業務場景綁定,耦合性強,不可公用
  • 消息數據與業務數據同庫,占用業務系統資源
  • 業務系統在使用關系型數據庫的情況下,消息服務性能會受到關系型數據庫並發性能的局限

 

六、MQ事務消息:

1、MQ事務消息的執行流程:

        基於MQ的分布式事務方案本質上是對本地消息表的封裝,整體流程與本地消息表一致,唯一不同的就是將本地消息表存在了MQ內部,而不是業務數據庫中,如下圖:

         由於將本地消息表存在了MQ內部,那么MQ內部的處理尤為重要,下面主要基於 RocketMQ4.3 之后的版本介紹 MQ 的分布式事務方案

2、RocketMQ事務消息:

        在本地消息表方案中,保證事務主動方發寫業務表數據和寫消息表數據的一致性是基於數據庫事務,而 RocketMQ 的事務消息相對於普通 MQ提供了 2PC 的提交接口,方案如下:

 (1)正常情況:

在事務主動方服務正常,沒有發生故障的情況下,發消息流程如下:

  • 步驟①:發送方向 MQ Server(MQ服務方)發送 half 消息
  • 步驟②:MQ Server 將消息持久化成功之后,向發送方 ack 確認消息已經發送成功
  • 步驟③:發送方開始執行本地事務邏輯
  • 步驟④:發送方根據本地事務執行結果向 MQ Server 提交二次確認(commit 或是 rollback)。
  • 最終步驟:MQ Server 如果收到的是 commit 操作,則將半消息標記為可投遞,MQ訂閱方最終將收到該消息;若收到的是 rollback 操作則刪除 half 半消息,訂閱方將不會接受該消息

(2)異常情況:

        在斷網或者應用重啟等異常情況下,圖中的步驟④提交的二次確認超時未到達 MQ Server,此時的處理邏輯如下:

  • 步驟⑤:MQ Server 對該消息發起消息回查
  • 步驟⑥:發送方收到消息回查后,需要檢查對應消息的本地事務執行的最終結果
  • 步驟⑦:發送方根據檢查得到的本地事務的最終狀態再次提交二次確認。
  • 最終步驟:MQ Server基於 commit/rollback 對消息進行投遞或者刪除。

3、MQ事務消息的優缺點:

(1)優點:相比本地消息表方案,MQ 事務方案優點是:

  • 消息數據獨立存儲 ,降低業務系統與消息系統之間的耦合
  • 吞吐量大於使用本地消息表方案

(2)缺點:

  • 一次消息發送需要兩次網絡請求(half 消息 + commit/rollback 消息) 。
  • 業務處理服務需要實現消息狀態回查接口。

 

七、最大努力通知:

        最大努力通知也稱為定期校對,是對MQ事務方案的進一步優化。它在事務主動方增加了消息校對的接口,如果事務被動方沒有接收到主動方發送的消息,此時可以調用事務主動方提供的消息校對的接口主動獲取

         在可靠消息事務中,事務主動方需要將消息發送出去,並且讓接收方成功接收消息,這種可靠性發送是由事務主動方保證的;但是最大努力通知,事務主動方僅僅是盡最大努力(重試,輪詢....)將事務發送給事務接收方,所以存在事務被動方接收不到消息的情況,此時需要事務被動方主動調用事務主動方的消息校對接口查詢業務消息並消費,這種通知的可靠性是由事務被動方保證的。

        所以最大努力通知適用於業務通知類型,例如微信交易的結果,就是通過最大努力通知方式通知各個商戶,既有回調通知,也有交易查詢接口。

 

八、各方案常見使用場景總結:

  • 2PC/3PC:依賴於數據庫,能夠很好的提供強一致性和強事務性,但延遲比較高,比較適合傳統的單體應用,在同一個方法中存在跨庫操作的情況,不適合高並發和高性能要求的場景。
  • TCC:適用於執行時間確定且較短,實時性要求高,對數據一致性要求高,比如互聯網金融企業最核心的三個服務:交易、支付、賬務。
  • 本地消息表/MQ 事務:適用於事務中參與方支持操作冪等,對一致性要求不高,業務上能容忍數據不一致到一個人工檢查周期,事務涉及的參與方、參與環節較少,業務上有對賬/校驗系統兜底。
  • Saga 事務:由於 Saga 事務不能保證隔離性,需要在業務層控制並發,適合於業務場景事務並發操作同一資源較少的情況。Saga 由於缺少預提交動作,導致補償動作的實現比較麻煩,例如業務是發送短信,補償動作則得再發送一次短信說明撤銷,用戶體驗比較差。所以,Saga 事務較適用於補償動作容易處理的場景

 

 

參考文章:https://blog.csdn.net/qq_34162294/article/details/120984951

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM