前言
Seata是阿里開源的分散式事務解決方案,本文將詳細介紹 Seata 的事務模式、原理以及使用。瞭解之前需清楚什麼是分散式事務。
一、什麼是 Seata
Seata 是一款開源的分散式事務解決方案,致力於提供高效能和簡單易用的分散式事務服務。Seata 將為使用者提供了XA、AT、TCC 和 SAGA 事務模式,為使用者打造一站式的分散式解決方案。
Seata 的幾種角色:
角色 | 說明 |
---|---|
TC | Transaction Coordinator,事務協調者,用來協調全域性和各個分支事務(不同服務)的狀態, 驅動它們的回滾或提交。 |
TM | Transaction Manager,事務管理者,業務層中用來開啟/提交/回滾一個整體事務(在呼叫服務的方法中用註解開啟事務)。 |
RM | Resource Manager,資源管理者,管理分支事務,與 TC 進行協調註冊分支事務,並且彙報分支事務的狀態,驅動分支事務的提交或回滾。 |
簡單流程圖:
二、事務模式
1. XA 模式
Seata 的 XA 模式大體與 2PC 事務相似。
1.1 流程介紹
第一階段:
- RM 註冊分支事務到 TC;
- RM 執行分支業務的 SQL 但不提交;
- RM 報告執行狀態到 TC;
第二階段:
- TC 檢測檢測各分支事務狀態,判斷整體事務提交或回滾;
- RM 接受 TC 的指令,進行統一的提交或回滾操作。
1.2 XA 優缺點
優點:
- 事務強一致性,滿足 ACID 原則;
- 實現簡單,無程式碼入侵。
缺點:
- 一階段鎖定資源,二階段結束才釋放,效能較差;
- 依賴關係型資料庫實現事務;
2. AT 模式
Auto Transaction,基於 XA 演進而來,需要資料庫支援,如果是 MySQL,則需要5.6以上版本才支援XA協議。
是一種無侵入的分散式事務解決方案,該模式下,使用者只需關注自己的業務 SQL,Seata 框架會在第一階段攔截並解析 SQL,生成 undo log,並自動生成事務二階段的提交和回滾操作。
AT 模式下,是利用快照實現資料回滾,屬於弱一致。
2.1 流程介紹
第一階段:
- RM 註冊分支事務到 TC;
- 記錄 undo log(資料快照);
- RM 執行分支業務的 SQL 並提交;
- RM 報告執行狀態到 TC;
第二階段:
- TC 檢測檢測各分支事務狀態,判斷整體事務提交或回滾;
- RM 接受 TC 的指令,進行統一的提交或回滾操作。
- 提交時,非同步刪除相應分支的 undo log;
- 回滾時,根據 undo log 生成補償回滾的 SQL,執行分支回滾並返回結果給 TC;
例如,一個分支業務需要對account
餘額表中的money
進行扣減 10 元,則需要進行如下流程:
2.2 髒寫問題
如下圖所示,併發事務之間,可能會產生髒寫導致資料修改被覆蓋。
如何解決髒寫,Seata 透過全域性鎖來管理事務,持有全域性鎖的事務才有執行 SQL 的權利,這裡全域性鎖只針對交由 Seata 管理的事務。
如下圖,簡單流程大致如下:
- 一階段本地事務提交前,需要確保先拿到全域性鎖 ;
- 拿不到全域性鎖 ,不能提交本地事務。
- 拿不到全域性鎖會重試,次數有限,超出限制將放棄,並回滾本地事務,釋放本地鎖。
2.3 資料快照
那麼非 Seata 事務於 Seata 事務併發修改資料時如何處理?
RM 在第一階段將分支事務註冊到 TC 時,會在 undo log 儲存兩個資料快照,分別是:
before-image
:資料修改前的快照after-image
:資料修改後的快照
當發生異常時,before-image
用來做資料回滾,after-image
用來判斷修改後資料於當前資料是否相同,相同則透過before-image
做資料回滾,不同則說明被其他非 Seata 事務修改過,記錄異常,人工介入。
具體流程見下圖。
2.4 髒讀問題
與髒寫類似,是指在全域性事務未提交前,被其它業務讀到已提交的分支事務的資料,本質上 Seata 預設的全域性事務是讀未提交。
那麼怎麼避免髒讀現象呢?
- 業務查詢時要使用
@GlobalTransactional
或@GlobalLock
來修飾查詢方法的呼叫; - 查詢語句須使用
select for update
語句。
這樣在執行 SQL 前會檢查全域性鎖是否存在,只有當全域性鎖完成之後,才能繼續執行 SQL,這樣就防止了髒讀。
不過,AT 事務模式下讀已提交的成本很高,對於非必要場景還是要儘量避免使用。
傳統的讀已提交不需要本地鎖,但這裡卻需要select for update
語句,查詢多出了加鎖和競爭的開銷,另外還要持鎖呼叫 TC 的lockQuery介面以判斷全域性鎖情況。
2.5 AT 優缺點
優點:
- 一階段直接完成事務提交,釋放資料庫資源,效能比較好;
- 利用全域性鎖實現讀寫隔離;
- 沒有程式碼入侵,框架自動完成回滾或提交。
缺點:
- 兩階段之間屬於軟狀態,屬於最終一直;
- 資料快照會影響效能,但比 XA 模式要好很多;
3. TCC 模式
關於什麼是 TCC 模式及原理,詳情見什麼是分散式事務。
TCC 與 AT 模式很相似,每階段都是獨立事務,不同的是 TCC 透過人工編碼來實現資料恢復。
3.1 流程介紹
TCC 每個階段是做什麼的:
Try
:資源的檢測和預留;Confirm
:完成資源操作業務,要求Try
成功,Confirm
一定能成功;Cancel
:預留資源釋放,可以理解為Try
的反向操作。
TCC 不存在資源阻塞的問題,因為每個方法都直接進行事務的提交,一旦出現異常透過則Cancel
來進行回滾補償,這也就是常說的補償性事務
舉例,一個扣減使用者癒合的業務,假設賬戶 A 原來的餘額是 100,需要扣減 30 元。
空回滾和業務懸掛
什麼是空回滾?
分支事務Try
操作阻塞時,可能導致全域性事務超時觸發Cancel
操作。在Try
未執行時先執行了Cancel
,這時的Cancel
理論上不應該回滾,這時就需要空回滾。
什麼是業務懸掛?
對於已經空回滾的業務,這時如果執行緒不再阻塞,繼續執行Try
,但不可能Confirm
或Cancel
,這就是業務懸掛,需要避免空回滾後的Try
操作。
如何解決空回滾和業務懸掛?
回滾時需要在執行Cancel
操作時,判斷有沒有執行Try
操作。相應的,在執行Try
時判斷有沒有該事務是否回滾過。
這裡,我們假設需要在凍結金額的時候進行事務操作。為了實現空回滾,防止業務懸掛,以及冪等性要求。我們必須在資料庫記錄凍結金額的同時,記錄當前事務 ID 和執行狀態,凍結金額表如下設計:
CREATE TABLE 'account_freeze_tbl'(
'xid' varchar (128) NOT NULL,
'user_id' varchar(255) DEFAULT NULL COMMENT '使用者id',
'freeze_money' int(11) unsigned DEFAULT '0' COMMENT '凍結金額',
'state' int(1) DEFAULT NULL COMMENT '事務狀態, O:try, 1:confirm, 2:cancel',
PRIMARY KEY ('xid') USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
表欄位設計完成後,執行如下的業務邏輯即可避免空回滾和業務懸掛。
3.2 TCC 優缺點
優點:
- 一階段直接完成事務提交,釋放資料庫資源,效能比較好;
- 相比 AT,無需生成快照和使用全域性鎖,效能最好;
- 不依賴資料庫事務,依賴補償操作,可用於非事務型資料庫。
缺點:
- 程式碼入侵,每個階段都需要編寫對應的業務程式碼;
- 軟狀態,屬於最終一致;
- 需要考慮
Confirm
和Cancel
的失敗情況,做好冪等處理。
4. Saga 模式
關於什麼是 Saga 模式及原理,詳情見什麼是分散式事務。
Saga 模式是 Seata 提供的長事務解決方案。也分為兩個階段:
- 一階段: 直接提交本地事務
- 二階段: 成功則什麼都不做;失敗則透過編寫補償業務來回滾
優點:
- 事務參與者可以基於事件驅動實現非同步呼叫,吞吐高;
- 一階段直接提交本地事務,無鎖,效能好;
- 程式碼入侵較 TCC 低,實現簡單。
缺點:
- 軟狀態持續時間不確定,時效性差;
- 沒有鎖和事務隔離,可能會有髒寫。
三、程式碼實現
具體程式碼使用,可參考 Seata 官方文件。
這裡需要注意每個模式需要的準備工作不同,如AT模式下就需要準備如下幾點:
- lock_table 匯入 Seata 資料庫,就是 TC 服務關聯的資料庫;
- undo_log 匯入業務相關的資料庫;
- 修改事務模式。
四、對比總結
對比維度 | XA | AT | TCC | Saga |
---|---|---|---|---|
資料一致性 | 強一致性 | 弱一致性 | 最終一致性 | 最終一致性 |
隔離性 | 完全隔離 | 基於全域性鎖 | 基於資源預留 | 無隔離 |
程式碼入侵 | 無 | 無 | 有 | 有 |
效能 | 低 | 較低 | 中 | 高 |
依賴本地事務 | 依賴 | 依賴 | 不依賴 | 不依賴 |
場景 | 一致性,隔離性要求高的業務場景。 | 繼續關係型資料庫的大多分散式事務的場景均適合。 | 對效能要求高,且有非關係型資料庫參與的事務。 | 業務流程較長,資料時效性要求較低的場景。 |
參考:
[1] B站黑馬. Seata從入門到進階.