目錄
- 一、概要
- 1. 分布式事務的概念
- 2. 分布式事務解決方案分類
- 二、常見的分布式事務解決方案
- 1. 基礎的 2PC(二階段提交)
- 1.1 核心思想
- 1.2 簡介
- 1.3 主要特點
- 1.3.1 優點
- 1.3.2 缺點
- 2. 基礎的 3PC(三階段提交)
- 2.1 核心思想
- 2.2 簡介
- 2.3 主要特點
- 2.3.1 優點
- 2.3.2 缺點
- 3. Seata - XA
- 3.1 核心思想
- 3.2 簡介
- 3.3 主要特點
- 3.3.1 優點
- 3.3.2 缺點
- 3.3.3 適用場景
- 4. Seata - AT
- 4.1 核心思想
- 4.2 簡介
- 4.3 主要特點
- 4.3.1 優點
- 4.3.2 缺點
- 4.3.3 適用場景
- 5. Seata - TCC
- 5.1 核心思想
- 5.2 簡介
- 5.3 主要特點
- 5.3.1 優點
- 5.3.2 缺點
- 5.3.3 適用場景
- 6. Seata - SAGA
- 6.1 核心思想
- 6.2 簡介
- 6.3 主要特點
- 6.3.1 優點
- 6.3.2 缺點
- 6.3.3 適用場景
- 7. 本地消息表
- 7.1 核心思想
- 7.2 簡介
- 7.3 主要特點
- 7.3.1 優點
- 7.3.2 缺點
- 7.3.3 適用場景
- 8. RocketMQ 事務消息
- 8.1 核心思想
- 8.2 簡介
- 8.3 主要特點
- 8.3.1 優點
- 8.3.2 缺點
- 8.3.3 適用場景
- 9. 最大努力通知
- 9.1 核心思想
- 9.2 簡介
- 9.3 主要特點
- 9.3.1 優點
- 9.3.2 缺點
- 9.3.3 適用場景
- 三、總結
- 1. 分布式事務解決方案的對比
- 2. 使用分布式事務中間件的弊端
一、概要
1. 分布式事務的概念
官話: 分布式事務就是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位于分布式系統的不同節點之上。簡單的說,就是一次大的操作由不同的小操作組成,這些小的操作分布在不同的服務器上,且屬于不同的應用,分布式事務需要保證這些小操作要么全部成成功,要么全部失敗。
白話: 本質上來說,分布式事務就是為了保證分散在各處的數據之間的一致性。
2. 分布式事務解決方案分類
分布式是否要考慮的第一個問題 —— 是否允許回滾:
- 若不允許回滾,則事務必須執行成功,不得執行失敗,因此只能采用異步通知型策略,且需要設計重試與冪等
- 若允許回滾,則需要考慮第二個問題 —— 是否要加鎖:
- 若要加鎖,則事務為剛性事務(CP - 強一致性)
- 若不加鎖,則事務為柔性事務(AP - 最終一致性),且只能采用同步補償策略
可以通過圖表的方式更加清晰地劃分各種不同的分布式事務解決方案和它們的設計思想:
接下來開始詳細講解這 9 種不同分布式事務解決方案的設計思想和優缺點。
二、常見的分布式事務解決方案
1. 基礎的 2PC(二階段提交)
1.1 核心思想
對分布式事務最暴力的做法就是讓所有參與者直接執行并提交本地事務,也就是一階段提交(1PC)。
但是這樣會導致事務失去回滾能力,各個參與者之間無法進行協同,極可能出現數據不一致的問題。
當然,也可以選擇放棄事務的回滾能力,要求事務必須執行成功,此時即為異步通知型事務,也即某種意義上的 1PC 模式。
為了給予分布式事務中的各個參與者協調與回滾的能力,自然地提出二階段提交的方案:
各個參與者先執行而不提交,若所有參與者都執行成功則提交,若有某個參與者執行失敗則其他參與者隨之回滾。
為了讓各個參與者(資源管理器)之間可以做到同步,需要一個中心節點進行統一調度,也就是事務管理器。
1.2 簡介
- Prepare 階段:
事務管理器給每個資源管理器發送 prepare 消息,資源管理器判斷是否可以執行事務。
若可以執行,則開啟本地事務,將事務寫入本地的 redo log 和 undo log,但是不提交,然后返回成功;
若不可以執行,則返回失敗。 - Commit 階段:
若事務管理器沒有收到任何資源管理器的失敗反饋,則給所有資源管理器發送 commit 消息;
若事務管理器收到了部分資源管理器的失敗反饋,則給所有資源管理器發送 rollback 消息。
資源管理器根據收到的消息執行本地事務的 commit 或 rollback。
1.3 主要特點
1.3.1 優點
- 靈活性:相比于不允許回滾的分布式事務解決方案,能夠允許事務的執行失敗,因而靈活度更高。
1.3.2 缺點
- 并發性能: 很低,在提交前,數據庫中的記錄是被排它鎖獨占的,因此并發性能會很低。
- 穩定性: 如果事務管理器發送故障,RM 無法收到 TM 下達的提交命令,則未提交的數據就會長時間被鎖。而此時,其他需要訪問被鎖數據的線程就會因無法訪問而進入阻塞,隨著阻塞線程越來越多,系統可能會崩潰。
核心的問題就在于對提交前數據的加鎖,既導致了并發性能低的問題,也導致了線程阻塞的問題。
能不能不加鎖?或能不能減少加鎖時間?大部分允許回滾的改進方案都是基于這兩個方面進行設計的。
2. 基礎的 3PC(三階段提交)
2.1 核心思想
3PC 模式試圖通過減少加鎖時間改進 2PC 模式。
為了解決 2PC 模式存在的問題,一個自然的想法就是引入超時機制,即資源管理器設等待超時時間,等待過久就自動提交或回滾,從而防止資源長期被鎖導致的并發問題和線程阻塞問題。
然而,關鍵的問題在于在等待超時后 RM 該如何進行決策?是應該默認提交還是默認回滾?
如果不能做出可靠的決策,直接給 2PC 添加超時機制的簡單做法極有可能導致數據不一致的問題。
為此,考慮在 2PC 模式的 prepare 階段前加一個階段,用于幫助超時后的默認操作進行決策。
2.2 簡介
- CanCommit 階段(新增階段):
事務管理器詢問每個資源管理器是否可以執行事務。
若可以執行,則資源管理器返回 Yes;若不可以執行,則資源管理器返回 No。 - PreCommit 階段(2PC 的 Prepare 階段):
若事務管理器收到的所有回復均為 Yes,則向所有資源管理器發送 PreCommit 消息;
若事務管理器收到的部分回復為 No,則向所有資源管理器發送 Abort 消息。
若資源管理器收到 PreCommit 消息,則執行事務但不提交;若資源管理器收到 Abort 消息,則中斷事務。
若某 RM 等待超時,則選擇中斷本地事務。分如下兩種情況討論。
- 實際上 TM 發送了 PreCommit 消息,但是該 RM 已中斷,其他 RM 也無法執行 DoCommit
階段,也就無法提交本地事務,因此全局事務失敗,不會發生數據不一致。- 實際上 TM 發送了 Abort 消息,則該 RM 執行的中斷是正確的操作,全局事務本應失敗,保證了數據一致。
- DoCommit 階段(2PC 的 Commit 階段):
若事務管理器收到了所有資源管理器在 PreCommit 階段返回的成功 ack,則向它們發送 doCommit 消息;
若事務管理器收到了某個資源管理器在 PreCommit 階段返回的失敗 ack,則向它們發送 rollback 消息。
資源管理器根據收到的消息執行本地事務的 commit 或 rollback。
若某 RM 等待超時,則選擇提交本地事務。因為全局事務可以執行到 DoCommit 階段,是因為在 CanCommit 階段所有 RM 都回應了 Yes(可以執行),因此可以認為全局事務大概率會執行成功,所以選擇提交本地事務,可以大概率保證數據一致性。
2.3 主要特點
2.3.1 優點
- 并發性能: 優于 2PC 模式,有效緩解了 2PC 模式中由于資源長期被鎖而產生的并發問題和線程阻塞問題。
2.3.2 缺點
- 數據一致性: 不能絕對保證,3PC 模式通過添加一個 CanCommit 階段來判斷在 DoCommit 階段等待超時的情況下應該如何決策。雖然相比于直接引入超時機制的 2PC 模式,能夠更大概率地保證數據一致性,但是并不能絕對地保證數據一致性。
3. Seata - XA
3.1 核心思想
直接基于數據庫的 XA 協議來忠實的實現 2PC(二階段提交)。
3.2 簡介
- 第一階段(prepare): 所有 RM 準備執行事務并鎖住所需的資源,然后各個 RM 向 TM 報告是否 ready。
- 第二階段(commit 或 rollback): 如果所有 RM 都是 ready ,則 TM 向所有參與者發送 commit 命令;如果有參與者 RM 不是 ready,則 TM 向所有參與者發送 rollback 命令。
3.3 主要特點
3.3.1 優點
- 使用難度: 非常簡單,因為是基于數據庫自帶特性實現,無需改動任何數據庫表,也無需開發任何額外代碼。
- 數據一致性: 強一致性,不會存在數據不一致的中間狀態,因為是 CP 模式,放棄了可用性 A。
3.3.2 缺點
- 并發性能: 很低,因為會長時間鎖資源,使其他線程被阻塞,而且各個 RM 之間會產生短板效應,所有 RM 都要鎖資源并等待最慢的 RM。
3.3.3 適用場景
并發量低,對數據的一致性要求很高,對中間結果較敏感的業務場景。
4. Seata - AT
4.1 核心思想
AT 是 Automatic Transaction 的縮寫,因為它能夠自動完成事務的提交與回滾操作,不需要額外的代碼開發。
其實現自動提交與回滾的方式如下:
TM 和 RM 所有數據庫里都要額外加一張表 UNDO_LOG,自動生成并存儲所有 SQL 的逆向 SQL 語句。
收到 TC 的分支提交,就刪除 UNDO_LOG;收到 TC 的分支回滾,就執行 UNDO_LOG 中存儲的逆向 SQL 語句。
本質上,Seata - AT 是通過補償的方式回滾事務(同步補償型),從而避免數據的加鎖。
4.2 簡介
執行本地業務和保存 redo/undo log 在同一個本地事務中,從而保證事務最終無論是 commit 還是 rollback 都必定能夠執行。
第一階段執行全部主要邏輯,第二階段主要做回滾或日志清理。
仍然屬于二階段提交,只是以數據庫加表為代價,自己模擬 MySQL 的 undo log 用于事務的回滾,從而避免加鎖。
4.3 主要特點
4.3.1 優點
- 并發性能: 較高,因為本質上是同步補償型事務,所以不用長時間鎖定資源。
- 使用難度: 簡單,因為是靠 Seata 自己解析生成反向 SQL 并回滾,無需額外的代碼開發,只需添加數據庫表。
4.3.2 缺點
- 數據一致性: 會存在數據不一致的中間狀態,因為是保障最終一致性的柔性事務(AP),放棄了數據的強一致性,所以若事務需要回滾,則在補償前會存在數據不一致的中間狀態。
- 業務侵入度: 少量,因為所有數據庫都需要額外添加一個 UNDO_LOG 表。
4.3.3 適用場景
高并發,允許數據出現短時不一致的業務場景。
5. Seata - TCC
5.1 核心思想
TCC 是 Try、Confirm、Cancel 三個單子的首字母縮寫,本質上是二階段模式(Confirm、Cancel 在二階段二選一)
設計理念上希望 90% 的事在 T 階段(第一階段)做完,CC 階段(第二階段)只做簡單操作。
然而,TCC 模式可能需要改動數據庫表,還需要自己額外編寫 3 個接口,以實現其復雜邏輯。
5.2 簡介
- Try 階段: 嘗試執行,完成所有業務檢查,預留所需的業務資源。
- Confirm 階段: 確認執行,真正執行業務,不需要再進行業務檢查,直接使用 Try 階段預留的業務資源(Confirm 失敗后需要進行重試,因此 Confirm 操作要有冪等設計)。
- Cancel 階段: 取消執行,釋放 Try 階段預留的業務資源(Cancel 操作也要有冪等設計)。
5.3 主要特點
5.3.1 優點
- 并發性能: 高,因為在數據庫中額外存儲準備狀態的數據,不用長時間鎖定資源。
- 靈活性: 高,各個分布式節點可以混用各種不同數據庫,因為 Try、Confirm 和 Cancel 操作的具體邏輯都是自己實現。
5.3.2 缺點
- 數據一致性: 存在數據不一致的中間狀態,因為是保障最終一致性的柔性事務(AP),放棄了數據的強一致性。
- 使用難度: 非常困難,TCC 模式中,Seata 只負責全局事務的提交與回滾指令,具體的提交與回滾操作全部需要開發人員自己實現,從而產生大量額外工作。
- 業務侵入度: 高,因為可能涉及到數據庫表的改動,自定義的 Confirm 和 Cancel 操作也容易對業務邏輯造成影響。
5.3.3 適用場景
高并發,允許數據出現短時不一致的業務場景。
6. Seata - SAGA
6.1 核心思想
考慮需要與外部第三方進行交互的事務,例如:調用支付寶支付接口 -> 出庫失敗 -> 調用支付寶退款接口
此時無法對外部系統的內部進行操作,只能通過接口調用進行交互,因此給每個操作實現一個逆向操作,這樣就可以通過調用第三方的逆向操作接口來進行回滾。
6.2 簡介
給每個操作實現一個逆向操作,通過逆向操作接口來進行回滾。
將長事務拆分為多個本地短事務,由事務管理器協調。如果某個步驟失敗,則根據相反的順序依次調用補償操作。
本質上是同步補償型的二階段模式。
6.3 主要特點
6.3.1 優點
- 并發性能: 較高,因為無需對資源加鎖,而是直接提交本地短事務,若需回滾則執行逆向操作。
6.3.2 缺點
- 數據一致性: 存在數據不一致的中間狀態,因為是保障最終一致性的柔性事務(AP),放棄了數據的強一致性。
- 使用難度: 困難,因為本地短事務的提交操作和逆向補償操作都需要開發者自己實現。
6.3.3 適用場景
需要與外部第三方進行交互,只能通過接口調用進行事務的回滾操作的場景。
異步通知型方案:可靠消息 VS 最大努力通知
都是異步通知型,區別在于消息的發送方和接收方誰來保證消息必定被傳達。
- 可靠消息:由發送方保證。
- 最大努力通知:由接收方保證。
可靠消息的兩個關鍵要求:
- 事務發起方一定能夠將消息成功發送出去(讓事務發起方的本地事務與能夠消息協同)
- 事務參與方一定能夠成功接收到消息(讓事務參與方的本地事務與能夠消息協同) 為了滿足可靠消息的兩個要求,必須有一個節點負責落庫并維護消息, 由事務發起方維護即為本地消息表,由消息中間件維護即為 MQ 事務消息。
7. 本地消息表
7.1 核心思想
通過讓事務發起方落庫并維護消息的方式,滿足可靠消息模式的兩個關鍵要求。
- 要求 1: 將本地消息表的落庫與發起方的本地事務放入同一個本地事務中,從而保證發起方本地事務的執行成功與本地消息的落庫成功保持原子性。
- 要求 2: 本地消息表維護消息的狀態,對于“未完成”狀態的消息,不斷重試發送,直到收到參與方對該消息的回執,從而保證消息必定能夠被參與方成功接收。
7.2 簡介
- 寫業務數據和寫消息數據在一個本地事務中,從而保證了業務操作和發送消息的原子性。
- 生產者定時掃描本地消息表,將未刪除的消息進行重發,從而保證所有消息都會發送給 MQ。
- 生產者收到 MQ 的回執 ACK 之后刪除本地消息表中的對應消息,從而保證所有消息都被 MQ 成功接收。
- MQ 會將未能成功發送給消費者的消息重新發送,從而保證所有消息都會發送給消費者。
7.3 主要特點
7.3.1 優點
- 靈活性: 不依賴任何分布式事務中間件,完全依靠開發者自己實現的可靠消息模式。
7.3.2 缺點
- 使用難度: 中等,需要在數據庫中添加本地消息表,并實現其增刪改查和定時掃碼重發、回執接收等功能。
- 數據一致性: 存在數據不一致的中間狀態,因為是保障最終一致性的柔性事務(AP),放棄了數據的強一致性。
- 業務入侵度: 較高,容易與業務耦合,不利于擴展和維護,難以實現通用。
7.3.3 適用場景
可以接受異步通知型方案,不希望依賴分布式事務中間件的業務場景。
8. RocketMQ 事務消息
8.1 核心思想
通過讓消息中間件落庫并維護消息的方式,滿足可靠消息模式的兩個關鍵要求。
- 要求 1: 發起方的本地事務執行前 MQ 落庫半消息,發起方的本地事務執行成功則 MQ 將半消息投遞到參與方,發起方的本地事務執行失敗則 MQ 將半消息丟棄,從而保證發起方本地事務的執行成功與本地消息的發送成功保持原子性。
- 要求 2: 由消息中間件對“未完成”的消息進行重試,直到收到參與方的回執,從而保證消息必定能夠被參與方成功接收。
8.2 簡介
- MQ 定期回查每個未 commit 或 rollback 的半消息對應的生產者的事務狀態,從而保證生產者方的本地事務必有一個終態。
- 消費者完成本地事務后需要將 MQ 中對應的消息標記為已消費,從而保證消費者必定完成每個消息對應的事務。
因此,所以使用 RocketMQ 的事務消息需要自己實現兩個方法:
- 執行本地事務的方法:在發起方的本地事務的末尾需要給 MQ 發 commit / rollback 的消息。
- 查詢本地事務狀態的方法:提供給 MQ,用于對發起方的本地事務狀態進行回查。
8.3 主要特點
8.3.1 優點
- 業務侵入度: 業務系統與消息系統的耦合度明顯低于本地消息表模式。
- 使用難度: 比較簡單,只需實現本地事務的執行與查詢兩個方法,消息的維護由中間件負責。
8.3.2 缺點
- 靈活性: 需要依賴消息中間件。
- 數據一致性: 存在數據不一致的中間狀態,因為是保障最終一致性的柔性事務(AP),放棄了數據的強一致性。
8.3.3 適用場景
可以接受異步通知型方案,對達到最終一致性的時間敏感度較低的業務場景。
9. 最大努力通知
9.1 核心思想
與可靠消息模式相反,最大努力通知模式由接收方保證消息必定被傳達。
事務發起方只負責“盡最大努力”通知事務參與方,但是并不保證消息的成功送達,事務參與方需要主動調用消息校對接口來確保事務主動方的消息被成功接收。
9.2 簡介
9.3 主要特點
9.3.1 優點
- 業務侵入度: 較低,事務主動方只需提供一個消息校對接口。
9.3.2 缺點
- 數據一致性: 存在數據不一致的中間狀態,因為是保障最終一致性的柔性事務(AP),放棄了數據的強一致性。
- 使用難度: 中等,需要額外的定期校驗機制對數據進行兜底,保證數據的最終一致性。
9.3.3 適用場景
跨平臺、跨企業的系統間業務交互,外部系統網絡環境復雜、不可信,對達到最終一致性的時間敏感度較低的業務場景。如充值平臺與運營商、支付對接、商戶通知等等跨平臺、跨企業的系統間業務交互場景。
三、總結
1. 分布式事務解決方案的對比
基礎 2PC | 基礎 3PC | Seata - XA | Seata - AT | Seata - TCC | Seata - SAGA | 本地消息表 | 事務消息 | 最大努力通知 | |
---|---|---|---|---|---|---|---|---|---|
一致性 | 強一致 | 強一致 | 強一致 | 最終一致 | 最終一致 | 最終一致 | 最終一致 | 最終一致 | 最終一致 |
并發性能 | 很低 | 較低 | 很低 | 較高 | 高 | 較高 | 中等 | 中等 | 較高 |
使用難度 | 簡單 | 中等 | 簡單 | 簡單 | 非常困難 | 困難 | 中等 | 比較簡單 | 中等 |
業務侵入 | 無侵入 | 無侵入 | 無侵入 | 少量 | 高 | 較高 | 較高 | 較低 | 較低 |
優點 | · 允許事務的執行失敗和回滾,因而靈活度更高 | · 業務無侵入 · 并發性能高于 2PC | · 使用簡單 · 強一致性,無數據不一致的中間狀態 | · 使用簡單 · 并發性能較高 | · 并發性能較高 · 可以混用數據庫 | · 并發性能較高 · 便于與外部第三方進行交互 | · 易于實現,使用簡單 · 不依賴任何分布式事務中間件 | · 對業務侵入較少 · 通過消息中間件解耦,下游事務異步化 | · 對業務侵入較少 |
缺點 | · 穩定性差,容易造成大量線程阻塞 | · 無法絕對保證數據一致性 | · 并發性能很低,會長時間鎖資源 | · 最終一致性,會存在數據不一致的中間狀態 · 有少量的業務侵入性 | · 最終一致性,會存在數據不一致的中間狀態 · 使用難度高,會帶來大量額外工作 · 業務侵入度高 | · 最終一致性,會存在數據不一致的中間狀態 · 使用難度較高,會帶來額外的開發工作 | · 最終一致性,會存在數據不一致的中間狀態 · 容易與業務耦合,難以實現通用 | · 最終一致性,會存在數據不一致的中間狀態 · 需要依賴消息中間件。 | · 最終一致性,會存在數據不一致的中間狀態 · 需要額外的定期校驗機制對數據進行兜底 |
適用業務 | · 要求強一致性 · 短事務 · 并發量低 | · 要求強一致性 · 短事務 · 并發量低 | · 要求強一致性 · 短事務 · 并發量低 | · 并發量高 · 允許出現短時間的數據不一致 | · 并發量高 · 允許出現短時間的數據不一致 | · 并發量高 · 允許出現短時間的數據不一致 · 需要與外部第三方進行交互 | · 可以接受異步通知型方案 · 允許出現短時間的數據不一致 · 不希望依賴分布式事務中間件 | · 可以接受異步通知型方案 · 允許出現短時間的數據不一致 | · 允許出現短時間的數據不一致 · 跨平臺、跨企業的系統間業務交互 · 外部系統網絡環境復雜、不可信 |
2. 使用分布式事務中間件的弊端
- 引入分布式事務中間件會造成系統大規模耦合。
- TC(事務協調者)作為基礎設施卻需要對外部的 TM(事務管理器)暴露,一定程度上違背了微服務的理念。
- 帶來了額外的學習成本與開發成本,提高了開發者的使用門檻,如果用不好反而會帶來更多棘手的問題。