本文分享自天翼雲開發者社區《分佈式架構基礎》,作者:胡****衝
基礎
事務
事務指的就是一個操作單元,在這個操作單元中的所有操作最終要保持一致的行為,要麼所有操作都成功,要麼所有的操作都被撤銷
本地事務
本地事物其實可以認為是數據庫提供的事務機制(ACID 原則)
分佈式事務
分佈式事務指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位於不同的分佈式系統的不同節點之上,分佈式事務就是為了保證不同數據庫的數據一致性
場景
- 單體系統訪問多個數據庫 :一個服務需要調用多個數據庫實例完成數據的增刪改操作
- 多個微服務調用同一個數據庫:多個服務需要調用一個數據庫實例完成數據的增刪改查
- 多個微服務訪問多個數據庫
方案
2PC
兩階段提交(2PC),對業務侵 ⼊很小,它最⼤的優勢就是對使⽤⽅透明,用户可以像使⽤本地事務⼀樣使⽤基於 XA 協議的分佈式事務,能夠嚴格保障事務 ACID 特性
(2PC) 的缺點也是顯而易見,它是一個強一致性的同步阻塞協議,事務執⾏過程中需要將所需資源全部鎖定,也就是俗稱的 剛性事務。所以它比較適⽤於執⾏時間確定的短事務,整體性能比較差
3PC
三段提交(3PC)是二階段提交(2PC)的一種改進版本 ,為解決兩階段提交協議的阻塞問題,上邊提到兩段提交,當協調者崩潰時,參與者不能做出最後的選擇,就會一直保持阻塞鎖定資源
2PC 中只有協調者有超時機制,3PC 在協調者和參與者中都引入了超時機制,協調者出現故障後,參與者就不會一直阻塞
雖然 3PC 用超時機 制,解決了協調者故障後參與者的阻塞問題,但與此同時卻多了一次網絡通信,性能上反而變得更差,也不太推薦
TCC
兩階段提交的一個變種,不同的是 TCC 為在業務層編寫代碼實現的兩階段提交。TCC 分別指 Try、Confirm、Cancel ,一個業務操作要對應的寫這三個方法
TCC 不存在資源阻塞的問題,因為每個方法都直接進行事務的提交,一旦出現異常通過則 Cancel 來進行回滾補償,這也就是常説的補償性事務
原本一個方法,現在卻需要三個方法來支持,TCC 對業務的侵入性很強,而且這種模式並不能很好地被複用,會導致開發量激增。還要考慮到網絡波動等原因,為保證請求一定送達都會有重試機制,所以考慮到接口的冪等性
消息事務(最終一致性)
消息事務其實就是基於消息中間件的兩階段提交,將本地事務和發消息放在同一個事務裏,保證本地操作和發送消息同時成功
- 訂單系統向 MQ 發送一條預備扣減庫存消息,MQ 保存預備消息並返回成功 ACK
- 接收到預備消息執行成功 ACK,訂單系統執行本地下單操作,為防止消息發送成功而本地事務失敗,訂單系統會實現 MQ 的回調接口,其內不斷的檢查本地事務是否執行成功,如果失敗則 rollback 回滾預備消息;成功則對消息進行最終 commit 提交。
- 庫存系統消費扣減庫存消息,執行本地事務,如果扣減失敗,消息會重新投,一旦超出重試次數,則本地表持久化失敗消息,並啓動定時任務做補償
基於消息中間件的兩階段提交方案,通常用在高併發場景下使用,犧牲數據的強一致性換取性能的大幅提升,不過實現這種方式的成本和複雜度是比較高的,還要看實際業務情況
Seata
也是從兩段提交演變而來的一種分佈式事務解決方案,提供了 AT、TCC、SAGA 和 XA 等事務模式,這裏重點介紹 AT模式
角色
- Transaction Coordinator(TC): 全局事務協調者,用來協調全局事務和各個分支事務(不同服務)的狀態, 驅動全局事務和各個分支事務的回滾或提交。
- Transaction Manager™: 事務管理者,業務層中用來開啓/提交/回滾一個整體事務(在調用服務的方法中用註解開啓事務)。
- Resource Manager(RM): 資源管理者,一般指業務數據庫代表了一個分支事務(Branch Transaction),管理分支事務與 TC 進行協調註冊分支事務並且彙報分支事務的狀態,驅動分支事務的提交或回滾
Seata 實現分佈式事務,設計了一個關鍵角色 UNDO_LOG (回滾日誌記錄表),我們在每個應用分佈式事務的業務庫中創建這張表,這個表的核心作用就是,將業務數據在更新前後的數據鏡像組織成回滾日誌,備份在 UNDO_LOG 表中,以便業務異常能隨時回滾
第一階段
把業務數據在更新前後的數據鏡像組織成回滾日誌,將業務數據的更新和回滾日誌在同一個本地事務中提交,分別插入到業務表和 UNDO_LOG 表中。在本地事務提交前,各分支事務需向全局事務協調者 TC 註冊分支 ( Branch Id) ,為要修改的記錄申請全局鎖 ,要為這條數據加鎖,利用 SELECT FOR UPDATE 語句。而如果一直拿不到鎖那就需要回滾本地事務。TM 開啓事務後會生成全局唯一的 XID,會在各個調用的服務間進行傳遞。
有了這樣的機制,本地事務分支(Branch Transaction)便可以在全局事務的第一階段提交,並馬上釋放本地事務鎖定的資源。相比於傳統的 XA 事務在第二階段釋放資源,Seata 降低了鎖範圍提高效率,即使第二階段發生異常需要回滾,也可以快速從UNDO_LOG 表中找到對應回滾數據並反解析成 SQL 來達到回滾補償
第二階段
根據各分支的決議做提交或回滾
- 如果決議是全局提交,此時各分支事務已提交併成功,這時 全局事務協調者(TC) 會向分支發送第二階段的請求。收到 TC 的分支提交請求,該請求會被放入一個異步任務隊列中,並馬上返回提交成功結果給 TC。異步隊列中會異步和批量地根據 Branch ID 查找並刪除相應 UNDO LOG 回滾記錄
- 如果決議是全局回滾,過程比全局提交麻煩一點,RM 服務方收到 TC 全局協調者發來的回滾請求,通過 XID 和 Branch ID 找到相應的回滾日誌記錄,通過回滾記錄生成反向的更新 SQL 並執行,以完成分支的回滾