1. 程式人生 > >分布式事務處理方式

分布式事務處理方式

模式 原因 數據庫操作 中間 成本高 ack 討論 但是 $$

分布式事務的典型處理方式

柔性事務和剛性事務

柔性事務滿足BASE理論(基本可用,最終一致)。

剛性事務滿足ACID理論。

在分布式事務當中主要討論的是柔性事務的處理方式。

柔性事務分為:

  • 兩階段提交型(2PC)
  • 三階段提交型(3PC)
  • 補償型(TCC、SAGA)

兩階段提交(2PC)型

兩階段提交(2-Phase Commit, 2PC)是一種比較簡單的分布式一致性協議。

2PC協議中,每個事務需要一個協調者來協調各個參與者。每個事務分為兩步執行。

  1. 階段一: 事務請求
    1. 協調者向所有參與者發送事務內容,詢問是否可以執行事務操作。
    2. 各參與者執行事務,寫事務日誌但不進行提交。 各參與者鎖定事務相關的資源,保證事務可以正常提交。
    3. 各參與者向協調者返回響應,YES表示可以提交,NO表示不可以提交。若協調者收到所有參與者的YES回復,則準備進行事務提交。若有參與者回復NO或者超時,則準備回滾事務。
  2. 階段二: 提交事務
    1. 協調者向所有參與者發送提交請求
    2. 參與者正式提交事務,並在完成後釋放相關資源。
    3. 參與者向協調者回復ACK,協調者收到所有參與者的ACK後認為事務提交成功。
  3. 回滾事務
    1. 在事務請求階段若有參與者回復NO或者超時,協調者向所有參與者發出回滾請求
    2. 各參與者執行事務回滾,並在完成後釋放相關資源。
    3. 參與者向協調者回復ACK,協調者收到所有參與者的ACK後認為事務回滾成功。

2PC是一種簡單的一致性協議,它存在一些問題:

  • 單點服務: 若協調者突然崩潰則事務流程無法繼續進行或者造成狀態不一致
  • 無法保證一致性: 若協調者第二階段發送提交請求時崩潰,可能部分參與者收到COMMIT請求提交了事務,而另一部分參與者未收到請求而放棄事務造成不一致現象。
  • 阻塞: 為了保證事務完成提交,各參與者在完成第一階段事務執行後必須鎖定相關資源直到正式提交,影響系統的吞吐量。

參與者在完成階段一的事務執行後等待協調者的下一個請求,若協調者超時則可以自行放棄事務。

這種方案仍然有無法保證一致性的缺點,但並不會出現某些資料所述一直鎖定資源,無法繼續的情況。

三階段提交(3PC)型

三階段提交協議(3-Phase Commit, 3PC)進一步將事務請求分為兩個階段,可以解決2PC協議阻塞的問題

但無法解決單點服務和不一致的問題。

3PC協議下事務分三步提交:

  1. CanCommit
    1. 協調者向所有參與者發送CanCommit請求
    2. 各參與者判斷是否可以完成事務提交,但不執行事務也不鎖定資源
    3. 各參與者根據是否可以完成事務向協調者回復YES或NO
  2. PreCommit
    1. 協調者向所有參與者發送PreCommit請求,執行事務預提交
    2. 各參與者執行事務,寫事務日誌但不進行提交。 各參與者鎖定事務相關的資源,保證事務可以正常提交。
    3. 各參與者向協調者返回響應。若協調者收到所有參與者的YES回復,則準備進行事務提交。若有參與者回復NO或者超時,則放棄事務。
  3. DoCommit
    1. 協調者向所有參與者發送提交請求
    2. 參與者正式提交事務,並在完成後釋放相關資源。
    3. 參與者向協調者回復ACK,協調者收到所有參與者的ACK後認為事務提交成功。若有參與者回復NO或者超時,則回滾事務。
    4. 參與者進入 PreCommit 狀態後,若始終未收到協調者的 DoCommit 請求則會超時後自動執行提交。

三階段提交協議在CanCommit階段不鎖定資源,解決了阻塞降低吞吐量的問題。

若某個參與者進入 PreCommit 後始終未收到協調者的進一步指令則會自動提交,該策略一定程度上避免協調者單點服務問題。

但是 3PC 仍然無法解決數據不一致問題。

事務補償型

TCC型事務(Try/Confirm/Cancel)可以歸為補償型。補償型的例子,在一個長事務( long-running )中 ,一個由兩臺服務器一起參與的事務,服務器A發起事務,服務器B參與事務,B的事務需要人工參與,所以處理時間可能很長。如果按照ACID的原則,要保持事務的隔離性、一致性,服務器A中發起的事務中使用到的事務資源將會被鎖定,不允許其他應用訪問到事務過程中的中間結果,直到整個事務被提交或者回滾。這就造成事務A中的資源被長時間鎖定,系統的可用性將不可接受。

WS-BusinessActivity提供了一種基於補償的long-running的事務處理模型。還是上面的例子,服務器A的事務如果執行順利,那麽事務A就先行提交,如果事務B也執行順利,則事務B也提交,整個事務就算完成。但是如果事務B執行失敗,事務B本身回滾,這時事務A已經被提交,所以需要執行一個補償操作,將已經提交的事務A執行的操作作反操作,恢復到未執行前事務A的狀態。這樣的SAGA事務模型,是犧牲了一定的隔離性和一致性的,但是提高了long-running事務的可用性。

TCC事務

TCC 將事務提交分為 Try - Confirm - Cancel 3個操作。

  • Try:預留業務資源/數據效驗
  • Confirm:確認執行業務操作
  • Cancel:取消執行業務操作,回收資源

TCC優點:讓應用自己定義數據庫操作的粒度,使得降低鎖沖突、提高吞吐量成為可能。

TCC不足之處:

  • 對應用的侵入性強。業務邏輯的每個分支都需要實現try、confirm、cancel三個操作,應用侵入性較強,改造成本高。
  • 實現難度較大。需要按照網絡狀態、系統故障等不同的失敗原因實現不同的回滾策略。為了滿足一致性的要求,confirm和cancel接口必須實現冪等

流程:

技術分享圖片

  1. 發起方發送 try 給所有參與者
  2. 參與者執行 try, 嘗試預留資源, 並返回給發起者
  3. 發起者接收所有參與者的返回信息
  4. 發起者發送 commit/cancel指令給參與者
  5. 參與者執行commit/cancel, 並返回執行結果
  6. 發起者接受參與者的結果

例子:

購買從 廣州到北京的機票, 因為購買不到直達的機票, 所以購買 廣州->上海->北京, 在上海中轉

廣州->上海 南航

上海->北京 東航

因為不屬於同一個航空公司, 所以需要分別購買

如果訂票系統依次購買, 可能存在第一家購買成功, 第二家購買失敗, 這事不能接受的

所以訂票系統先向兩家航空公司發送請求, 確定是否有足夠的余票, 並讓對方預留票

  • 如果兩邊都返回預留成功, 訂票系統就同時向兩邊發送請求, 進行確認購票
    • 如果訂票系統遲遲沒有成功發送確認指令, 預留的票會被航空公司自動取消
  • 如果任何一家航空公司返回預留失敗, 訂票系統就向所有航空公司發送請求, 進行取消所有已經預留的票

與 2PC 的比較

TCC 2PC
第一階段 Try: 請求原業務方預留資源 Prepare: 詢問是否可以進行提交
段二階段(成功) Confirm:確認執行 Commit: 提交事務
第二階段(失敗) Cancel: 取消執行資源操作 Rollback: 回滾事務

2PC 是資源層面的, 基於數據庫底層 (比如mysql的xa事務), 開發者不可感知, 無侵入性

TCC 是業務層面, 開發者可感知, 可以根據業務對事務做特定的優化

存在的問題

存在和 2PC 類似的問題

如果發起者發送 confirm 過程失敗, 導致有的參與者接收到指令, 執行了comfirm

而有的參與者由於沒有接收到指令, 而因為timeout, 執行了cancel

破壞了整個系統的數據一致性

Saga

背景:

一直以來, 執行數據庫事務都是使用 LLT (long lived transaction), 即跨越多個數據庫事務的事務, 一次性完成所有事務, 其中不允許其他事務打斷, 使用這種機制, 可以保證數據庫的數據一致性

但是會帶來幾個問題:

  1. LLT 可能會涉及到大量的數據庫實例, 需要對其進行封鎖, 不允許其他事務進行訪問, 但由於 LLT 常常需要消耗大量的時間, 可能造成其他事務長期得不到資源, 而處於阻塞狀態
  2. 可能出現兩個 LLT 各自封鎖了部分實例, 但是都達不到各自執行的要求, 兩方出現死鎖

簡述

而 Saga 模式是為了彌補 LLT 的缺陷

Saga 模式將一個長事務分割多個子事務 (saga), 然後逐一執行每一個子事務, 一旦其中一環出現了錯誤, 通過補償機制, 一一回滾之前已經執行的事務
$$
T1 T2 T3...Tj...Tn\
transaction\
C1 C2 C3...Cj...Cn \
compensating?transaction
$$
整個流程有以下情況:

  • 全部成功

$$
T1 → T2 → T3 →...→...→ Tn
$$

  • 中途失敗後回滾

$$
T1 → T2 → T3 →...→...→ Tj → Cj →...→ C3 → C2 → C1
$$

註意點:

  • Tj 和 Cj 是冪等的
  • 如果 Cj 執行不成功就需要人工介入
  • Tj-Cj 的結果應和 Cj - Tj 的執行結果相同

例子

技術分享圖片

假設一個人需要進行如上的飛機票預訂, 訂票系統需要向四個航空公司發出訂票請求

技術分享圖片

如果其中任何一個環節出現了問題, 就通過補償機制, 將之前預訂的票逐一退訂

與TCC的比較

  • 不強制參與者實現try接口, 只需要可以commitrollback
  • 不需要占有預留資源
  • 理想情況下, 只需要向每個參與者發送1次請求 (TCC需要2次)

分布式事務處理方式