原文請關注:性能與架構公眾號
1. Seata 概述
Seata 是 Simple Extensible Autonomous Transaction Architecture 的簡寫,由 feascar 改名而來。
Seata 是阿里開源的分布式事務框架,屬于二階段提交模式。
目前github上已經有 12267 顆星了,也很活躍,最新的提交時間很多都是幾天前。
首先我們回顧一下在單體應用中,例如一個業務調用了3個模塊,他們都使用同一個數據源,是靠本地事務來保證事務一致性。
但在微服務架構中,這3個模塊會變為3個獨立的微服務,各自有自己的數據源,調用邏輯就變為:
Seata 如何處理呢?
Business 是業務入口,在程序中會通過注解來說明他是一個全局事務,這時他的角色為 TM(事務管理者)。
Business 會請求 TC(事務協調器,一個獨立運行的服務),說明自己要開啟一個全局事務,TC 會生成一個全局事務ID(XID),并返回給 Business。
Business 得到 XID 后,開始調用微服務,例如調用 Storage。
(和上面的圖一樣,方便查看,防止滾到到這兒時已經看不到上面的圖片了)
Storage 會收到 XID,知道自己的事務屬于這個全局事務。Storage 執行自己的業務邏輯,操作本地數據庫。
Storage 會把自己的事務注冊到 TC,作為這個 XID 下面的一個分支事務,并且把自己的事務執行結果也告訴 TC。
此時 Storage 的角色是 RM(資源管理者),資源是指本地數據庫。
Order、Account 的執行邏輯與 Storage 一致。
在各個微服務都執行完成后,TC 可以知道 XID 下各個分支事務的執行結果,TM(Business) 也就知道了。
Business 如果發現各個微服務的本地事務都執行成功了,就請求 TC 對這個 XID 提交,否則回滾。
TC 收到請求后,向 XID 下的所有分支事務發起相應請求。
各個微服務收到 TC 的請求后,執行相應指令,并把執行結果上報 TC。
重要機制
(1)全局事務的回滾是如何實現的呢?
Seata 有一個重要的機制:回滾日志。
每個分支事務對應的數據庫中都需要有一個回滾日志表 UNDO_LOG,在真正修改數據庫記錄之前,都會先記錄修改前的記錄值,以便之后回滾。
在收到回滾請求后,就會根據 UNDO_LOG 生成回滾操作的 SQL 語句來執行。
如果收到的是提交請求,就把 UNDO_LOG 中的相應記錄刪除掉。
(2)RM 是怎么自動和 TC 交互的?
是通過監控攔截JDBC實現的,例如監控到開啟本地事務了,就會自動向 TC 注冊、生成回滾日志、向 TC 匯報執行結果。
(3)二階段回滾失敗怎么辦?
例如 TC 命令各個 RM 回滾的時候,有一個微服務掛掉了,那么所有正常的微服務也都不會執行回滾,當這個微服務重新正常運行后,TC 會重新執行全局回滾。
1.3 核心組件
回顧一下其中的核心組件:
- 事務協調器 TC
維護全局和分支事務的狀態,指示全局提交或者回滾。
- 事務管理者 TM
開啟、提交或者回滾一個全局事務。
- 資源管理者 RM
管理執行分支事務的那些資源,向TC注冊分支事務、上報分支事務狀態、控制分支事務的提交或者回滾。
1.4 具體工作過程
再從宏觀上梳理一下 Seata 的工作過程:
- TM 請求 TC,開始一個新的全局事務,TC 會為這個全局事務生成一個 XID。
- XID 通過微服務的調用鏈傳遞到其他微服務。
- RM 把本地事務作為這個XID的分支事務注冊到TC。
- TM 請求 TC 對這個 XID 進行提交或回滾。
- TC 指揮這個 XID 下面的所有分支事務進行提交、回滾。
2. Seata 詳細工作流程示例
下面我們通過一個分支事務的執行過程來了解 Seata 的工作流程。
例如有一個業務表 product(id,name),分支事務的業務邏輯:
update product set name = 'GTS' where name = 'TXC';
2.1 一階段
(1)解析 SQL
得到 SQL 的類型(UPDATE),表(product),條件(where name = 'TXC')等相關的信息。
(2)查詢前鏡像
根據解析得到的條件信息,生成查詢語句,定位數據。
select id, name from product where name = 'TXC';
得到前鏡像:
(3)執行業務 SQL
執行自己的業務邏輯:
update product set name = 'GTS' where name = 'TXC';
把 name 改為了 GTS。
(4)查詢后鏡像
根據前鏡像的結果,通過 主鍵 定位數據。
select id, name from product where id = 1;
得到后鏡像:
(5)插入回滾日志
把前后鏡像數據以及業務 SQL 相關的信息組成一條回滾日志記錄,插入到 UNDO_LOG 表中。
(6)提交前,向 TC 注冊分支:申請 product 表中,主鍵值等于 1 的記錄的 全局鎖 。
(7)本地事務提交:業務數據的更新和前面步驟中生成的 UNDO LOG 一并提交。
(8)將本地事務提交的結果上報給 TC。
2.2 二階段 - 回滾
(1)收到 TC 的分支回滾請求,開啟一個本地事務,執行如下操作。
(2)通過 XID 和 Branch ID 查找到相應的 UNDO LOG 記錄。
(3)數據校驗
拿 UNDO LOG 中的后鏡與當前數據進行比較,根據校驗結果決定是否做回滾。
(4)根據 UNDO LOG 中的前鏡像和業務 SQL 的相關信息生成并執行回滾的語句:
update product set name = 'TXC' where id = 1;
(5)提交本地事務
并把本地事務的執行結果(即分支事務回滾的結果)上報給 TC。
二階段 - 提交
(1)收到 TC 的分支提交請求,把請求放入一個異步任務的隊列中,馬上返回提交成功的結果給 TC。
(2)異步任務階段的分支提交請求,將異步和批量地刪除相應 UNDO LOG 記錄。
3. 小結
上面介紹的是 Seata 的 AT 模式,就是自動化事務,使用非常簡單,對業務代碼沒有侵入性。
不足的地方是目前文檔比較少,網上的相關材料也不是很多,所以使用過程中遇到問題時可能就需要自己查看源碼,分析原理。
Seata 還支持 TCC 和 Saga 模式,但支持的主要方式是 AT。