分布式事務方案匯總及Seata詳解
引言
隨著業務量的不斷增長,單體架構漸漸扛不住巨大的流量,此時就需要對服務進行 拆分 ,數據庫、表做 分庫分表 處理。以訂單系統為例,也就產生了訂單中心、用戶中心、庫存中心等,由此帶來的問題就是業務間相互隔離,每個業務都維護著自己的數據庫,數據的交換只能進行 RPC 調用。
當用戶再次下單時,需同時對訂單庫 order、庫存庫 storage、用戶庫 account 進行操作,可此時我們只能保證自己本地的數據一致性,無法保證調用其他服務的操作是否成功,所以為了保證整個下單流程的數據一致性,就需要分布式事務介入。
分布式事務方案一覽
實現分布式事務的方案比較多,常見的比如基于 XA 協議的 2PC、3PC,基于業務層的 TCC,還有應用消息隊列 + 消息表實現的最終一致性方案。
- 2PC
基于 XA 協議實現的分布式事務,XA 協議中分為兩部分:事務管理器和本地資源管理器。其中本地資源管理器往往由數據庫實現,比如 Oracle、MYSQL 這些數據庫都實現了 XA 接口,而事務管理器則作為一個全局的調度者。
兩階段提交(2PC),對業務侵?很小,它最?的優勢就是對使??透明,用戶可以像使?本地事務?樣使?基于 XA 協議的分布式事務,能夠嚴格保障事務 ACID 特性。
2PC的缺點也是顯而易見,它是一個強一致性的同步阻塞協議,事務執?過程中需要將所需資源全部鎖定,也就是俗稱的 剛性事務。所以它比較適?于執?時間確定的短事務,整體性能比較差。
一旦事務協調者宕機或者發生網絡抖動,會讓參與者一直處于鎖定資源的狀態或者只有一部分參與者提交成功,導致數據的不一致。因此,在?并發性能?上的場景中,基于 XA 協議的分布式事務并不是最佳選擇。
- 3PC
三段提交(3PC)是二階段提交(2PC)的一種改進版本 ,為解決兩階段提交協議的阻塞問題,上邊提到兩段提交,當協調者崩潰時,參與者不能做出最后的選擇,就會一直保持阻塞鎖定資源。
2PC 中只有協調者有超時機制,3PC 在協調者和參與者中都引入了超時機制,協調者出現故障后,參與者就不會一直阻塞。而且在第一階段和第二階段中又插入了一個準備階段(如下圖),保證了在最后提交階段之前各參與節點的狀態是一致的。
雖然 3PC 用超時機制,解決了協調者故障后參與者的阻塞問題,但與此同時卻多了一次網絡通信,性能上反而變得更差,也不太推薦。
- TCC
所謂的 TCC 編程模式,也是兩階段提交的一個變種,不同的是 TCC 為在業務層編寫代碼實現的兩階段提交。TCC 分別指 Try、Confirm、Cancel ,一個業務操作要對應的寫這三個方法。
以下單扣庫存為例,Try 階段去占庫存,Confirm 階段則實際扣庫存,如果庫存扣減失敗 Cancel 階段進行回滾,釋放庫存。
TCC 不存在資源阻塞的問題,因為每個方法都直接進行事務的提交,一旦出現異常通過則 Cancel 來進行回滾補償,這也就是常說的補償性事務。
原本一個方法,現在卻需要三個方法來支持,可以看到 TCC 對業務的侵入性很強,而且這種模式并不能很好地被復用,會導致開發量激增。還要考慮到網絡波動等原因,為保證請求一定送達都會有重試機制,所以考慮到接口的冪等性。
- 消息事務(最終一致性)
消息事務其實就是基于消息中間件的兩階段提交,將本地事務和發消息放在同一個事務里,保證本地操作和發送消息同時成功。
下單扣庫存原理圖:
- 訂單系統向 MQ 發送一條預備扣減庫存消息,MQ 保存預備消息并返回成功 ACK
- 接收到預備消息執行成功 ACK,訂單系統執行本地下單操作,為防止消息發送成功而本地事務失敗,訂單系統會實現 MQ 的回調接口,其內不斷的檢查本地事務是否執行成功,如果失敗則 rollback 回滾預備消息;成功則對消息進行最終 commit 提交。
- 庫存系統消費扣減庫存消息,執行本地事務,如果扣減失敗,消息會重新投,一旦超出重試次數,則本地表持久化失敗消息,并啟動定時任務做補償。
基于消息中間件的兩階段提交方案,通常用在高并發場景下使用,犧牲數據的強一致性換取性能的大幅提升,不過實現這種方式的成本和復雜度是比較高的,還要看實際業務情況。
基于RokectMQ實現最終一致性具體實現可參考:訂單系統中的數據一致性方案及RocketMQ事務消息詳解
分布式事務Seata
Seata 也是從兩段提交演變而來的一種分布式事務解決方案,提供了 AT、TCC、SAGA 和 XA 等事務模式。
- XA模式:強一致性分階段事務模式,犧牲了一定的可用性,無業務侵入
- TCC模式:最終一致的分階段事務模式,有業務侵入
- AT模式:最終一致的分階段事務模式,無業務侵入,也是Seata的默認模式
- SAGA模式:長事務模式,有業務侵入
Seata事務管理中有三個重要的角色:
- TC (Transaction Coordinator) - 事務協調者:維護全局和分支事務的狀態,協調全局事務提交或回滾。
- TM (Transaction Manager) - 事務管理器:定義全局事務的范圍、開始全局事務、提交或回滾全局事務。
- RM (Resource Manager) - 資源管理器:管理分支事務處理的資源,與TC交談以注冊分支事務和報告分支事務的狀態,并驅動分支事務提交或回滾。
Seata實現原理
(1)XA模式
seata的XA模式做了一些調整,但大體相似:
RM一階段的工作:
- 注冊分支事務到TC
- 執行分支業務sql但不提交
- 報告執行狀態到TC
TC二階段的工作:
- TC檢測各分支事務執行狀態:如果都成功,通知所有RM提交事務;如果有失敗,通知所有RM回滾事務
RM二階段的工作:
- 接收TC指令,提交或回滾事務
XA模式
【XA模式實現】
Seata的starter已經完成了XA模式的自動裝配,實現非常簡單,步驟如下:
1、修改application.yml文件,開啟XA模式:
seata:
data-source-proxy-mode: XA # 開啟數據源代理的XA模式
2、給發起全局事務的入口方法添加@GlobalTransactional注解,
@GlobalTransactional
public Long create(Order order) {
// 創建訂單
orderMapper.insert(order);
// 扣余額 ...略
// 扣減庫存 ...略
return order.getId();
}
(2)AT模式
AT模式同樣是分階段提交的事務模型,但缺彌補了XA模型中資源鎖定周期過長的缺陷。
階段一RM的工作:
- 注冊分支事務
- 記錄undo-log(數據快照)
- 執行業務sql并提交
- 報告事務狀態
階段二提交時RM的工作:
- 刪除undo-log即可
階段二回滾時RM的工作:
- 根據undo-log恢復數據到更新前
AT模式
執行流程如下:
(3)TCC模式
TCC模式與AT模式非常相似,每階段都是獨立事務,不同的是TCC通過人工編碼來實現數據恢復。需要實現三個方法:
- Try:資源的檢測和預留;
- Confirm:完成資源操作業務;要求 Try 成功 Confirm 一定要能成功。
- Cancel:預留資源釋放,可以理解為try的反向操作。
TCC的工作模型圖:
TCC模式
【TCC的空回滾和業務懸掛】
當某分支事務的try階段阻塞時,可能導致全局事務超時而觸發二階段的cancel操作。在未執行try操作時先執行了cancel操作,這時cancel不能做回滾,就是空回滾。
對于已經空回滾的業務,如果以后繼續執行try,就永遠不可能confirm或cancel,這就是業務懸掛。應當阻止執行空回滾后的try操作,避免懸掛。
(4)Saga模式
Saga模式是SEATA提供的長事務解決方案。也分為兩個階段:
- 一階段:直接提交本地事務
- 二階段:成功則什么都不做;失敗則通過編寫補償業務來回滾
Saga模式優點:
- 事務參與者可以基于事件驅動實現異步調用,吞吐高
- 一階段直接提交事務,無鎖,性能好
- 不用編寫TCC中的三個階段,實現簡單
缺點:
- 軟狀態持續時間不確定,時效性差
- 沒有鎖,沒有事務隔離,會有臟寫
(5)模式對比
參考&感謝
https://www.cnblogs.com/chengxy-nds/p/14046856.html