分佈式事務處理方式

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

柔性事務和剛性事務

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

剛性事務知足ACID理論。sql

在分佈式事務當中主要討論的是柔性事務的處理方式。數據庫

柔性事務分爲:服務器

  • 兩階段提交型(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請求提交了事務,而另外一部分參與者未收到請求而放棄事務形成不一致現象。
  • 阻塞: 爲了保證事務完成提交,各參與者在完成第一階段事務執行後必須鎖定相關資源直到正式提交,影響系統的吞吐量。

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

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

三階段提交(3PC)型

三階段提交協議(3-Phase Commit, 3PC)進一步將事務請求分爲兩個階段,能夠解決2PC協議阻塞的問題但沒法解決單點服務和不一致的問題。blog

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接口必須實現冪等

流程:

image.png

  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 的執行結果相同

例子

img

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

img

若是其中任何一個環節出現了問題, 就經過補償機制, 將以前預訂的票逐一退訂

與TCC的比較

  • 不強制參與者實現try接口, 只須要能夠commitrollback
  • 不須要佔有預留資源
  • 理想狀況下, 只須要向每一個參與者發送1次請求 (TCC須要2次)
相關文章
相關標籤/搜索