分布式事務知識小結
1. 事務的ACID
-
原子性(Atomicity),可以理解為一個事務內的所有操作要么都執行,要么都不執行。
-
一致性(Consistency),可以理解為數據是滿足完整性約束的,也就是不會存在中間狀態的數據,比如你賬上有400,我賬上有100,你給我打200塊,此時你賬上的錢應該是200,我賬上的錢應該是300,不會存在我賬上錢加了,你賬上錢沒扣的中間狀態。
-
隔離性(Isolation),指的是多個事務并發執行的時候不會互相干擾,即一個事務內部的數據對于其他事務來說是隔離的。
- 持久性(Durability),指的是一個事務完成了之后數據就被永遠保存下來,之后的其他操作或故障都不會對事務的結果產生影響。
2. Redis的事務
-
Redis 的事務不能保證所有操作要么都執行要么都不執行。
-
Redis事務中的某個命令失敗了,之后的命令還是會被處理,Redis 不會停止命令,意味著也不會回滾。
- Redis認為如果命令出錯是語法使用錯誤,應該在開發的時候就被檢測出來,不應在生產環境中出現。回滾并不能免于編程錯誤。
3. 2PC
-
是一種強一致性設計
-
引入一個事務協調者的角色來協調管理各參與者(也可稱之為各本地資源)的提交和回滾,二階段分別指的是準備(投票)和提交兩個階段
-
正常情況
-
異常情況
-
協調者故障分析
- 2PC 是同步阻塞協議
- 協調者有超時機制
- 協調者是一個單點,存在單點故障問題
- 協調者在發送準備命令之前掛了,事務沒開始,無影響
- 協調者在發送準備命令之后掛了,處于事務資源鎖定的狀態,事務無法執行,阻塞其他操作
- 協調者在發送回滾事務命令之前掛了,事務無法繼續提交,第一階段準備成功的參與者都阻塞
- 協調者在發送回滾事務命令之后掛了,大的概率都會回滾成功,資源都會釋放。但是如果出現網絡分區問題,某些參與者將因為收不到命令而阻塞
- 協調者在發送提交事務命令之前掛了,資源都阻塞
- 協調者在發送提交事務命令之后掛了,大的概率都會回滾成功,資源都會釋放。但是如果出現網絡分區問題,某些參與者將因為收不到命令而阻塞
-
通過選舉得到新協調者
- 每個參與者自身的狀態只有自己和協調者知道,因此新協調者無法通過在場的參與者的狀態推斷出掛了的參與者是什么情況
- 極端情況下還是無法避免數據不一致問題
-
總結
- 同步阻塞的,盡量保證強一致性的分布式事務
- 總體而言效率低,并且存在單點故障問題,在極端條件下存在數據不一致的風險
- 不能做到數據一致,需要補償機制
4. 3PC
- 在參與者中也引入了超時機制
- CanCommit、PreCommit 和 DoCommit
- 正常情況
- 優勢
- 不會直接鎖資源而是先詢問情況
- 加入超時機制
- 如果是等待預提交命令超時,那該干啥就干啥了
- 如果是等待提交命令超時,那么參與者就會提交事務了
- 劣勢
- 多一個階段,性能會差一些,大部分情況資源都可用
- 超時機制依然存在數據不一致情況
- 比如在等待提交命令時候超時了,參與者默認執行的是提交事務操作,但是有可能執行的是回滾操作,這樣數據就不一致了
- 總結
- 引入預提交階段來使得參與者之間的狀態得到統一。如果調度者掛了,新協調者來的時候發現有一個參與者處于預提交或者提交階段,那么表明已經經過了所有參與者的確認了,所以此時執行的就是提交命令。
- 引入了參與者超時機制,但整體的交互過程更長了,性能有所下降,并且還是會存在數據不一致問題。因為掛了的參與者到底有沒有執行事務無法斷定。需要補償機制
5. TCC
-
TCC
- Try 指的是預留,即資源的預留和鎖定,注意是預留。
- Confirm 指的是確認操作,這一步其實就是真正的執行了。
- Cancel 指的是撤銷操作,可以理解為把預留階段的動作撤銷了。
-
思想上類似2PC,TCC 就是通過代碼人為實現了兩階段提交
-
TCC模型還有個事務管理者的角色,變成多點,引入集群。用來記錄TCC全局事務狀態并提交或者回滾事務
-
引入超時,超時后進行補償,并且不會鎖定整個資源
-
優勢
- TCC可以跨數據庫、跨不同的業務系統來實現事務
-
劣勢
- TCC 對業務的侵入較大和業務緊耦合
撤銷和確認操作的執行可能需要重試,因此還需要保證操作的冪等
6. 本地消息表
-
利用各系統本地的事務來實現分布式事務
-
有一張存放本地消息的表,一般都是放在數據庫中,然后在執行業務的時候將業務的執行和將消息放入消息表中的操作放在同一個事務中,保證消息放入本地表中業務肯定是執行成功的
-
后臺任務定時去讀取本地消息表,篩選出還未成功的消息再調用對應的服務,服務更新成功了再變更消息的狀態。
-
重試就得保證對應服務的方法是冪等的,而且一般重試會有最大次數,超過最大次數可以記錄下報警讓人工處理
-
實現的是最終一致性
7. 消息事務
-
利用MQ事務
-
先給 Broker 發送事務消息即半消息,半消息不是說一半消息,而是這個消息對消費者來說不可見,然后發送成功后發送方再執行本地事務
-
再根據本地事務的結果向 Broker 發送 Commit 或者RollBack命令
-
RocketMQ的發送方會提供一個反查事務狀態接口,如果一段時間內半消息沒有收到任何操作請求,那么 Broker 會通過反查接口得知發送方事務是否執行成功,然后執行Commit 或者RollBack命令。
-
如果是 Commit 那么訂閱方就能收到這條消息,然后再做對應的操作,做完了之后再消費這條消息即可
-
如果是RollBack那么訂閱方收不到這條消息,等于事務就沒執行過
-
消息事務實現的也是最終一致性
8. 最大努力通知
- 本地消息表也可以算最大努力,事務消息也可以算最大努力。
- 最大努力通知其實只是表明了一種柔性事務的思想:我已經盡力我最大的努力想達成事務的最終一致了。
- 適用于對時間不敏感的業務,例如短信通知。
9. Saga
-
長事務的解決方案,更適合于“業務流程長、業務流程多”的場景
-
特別是針對參與事務的服務是遺留系統服務,此類服務無法提供TCC模式下的三個接口,就可以采用Saga模式
-
針對每一個分布式事務的每個執行操作或者是步驟都是一個 Ti,例如扣減庫存是T1、創建訂單是T2、支付服務是T3。那么針對每個Ti都對應一個補償動作Ci,例如回復庫存C1、訂單回滾C2、支付回滾C3
-
優勢
- 一階段提交本地事務,無鎖,高性能
- 參與者可異步執行,高吞吐
- 補償服務易于實現,因為一個更新操作的反向操作是比較容易理解的
-
劣勢
- 不保證隔離性(可以看到其他事務)
- Saga操作模式更像發電子郵件,發出的電子郵件是正確的,接收者按正常方式解讀。如果郵件發錯了,就再發一封告訴接收者,請忽略上一封郵件。但有時候這個操作已經不可逆了。
- 舉例
- 分布式事務內先給用戶A充值,然后給用戶B扣減余額,如果在給A用戶充值成功,在事務提交以前,A用戶把余額消費掉了,如果事務發生回滾,這時則沒有辦法進行補償了。
- 應對方法
- 業務流程設計時遵循“寧可長款,不可短款”的原則,長款意思是客戶少了錢機構多了錢,以機構信譽可以給客戶退款,反之則是短款,少的錢可能追不回來了,所以在業務流程設計上一定是先扣款;
- 有些業務場景可以允許讓業務最終成功,在回滾不了的情況下可以繼續重試完成后面的流程,達到最終一致性的目的
- 不保證隔離性(可以看到其他事務)
-
兩種恢復策略
-
兩種模式
10. 總結
- 2PC 和 3PC 是一種強一致性事務,不過還是有數據不一致,阻塞等風險,而且只能用在數據庫層面。
- TCC 是一種補償性事務思想,適用的范圍更廣,在業務層面實現,因此對業務的侵入性較大,每一個操作都需要實現對應的三個方法
- Saga是一種長事務的解決方案,比TCC更簡單,但不能保證隔離性
- 本地消息、事務消息和最大努力通知其實都是最終一致性事務,因此適用于一些對時間不敏感的業務