# Seata 是什么?
Seata 是一款開源的分布式事務解決方案,致力于提供高性能和簡單易用的分布式事務服務。Seata 將為用戶提供了 AT、TCC、SAGA 和 XA 事務模式,為用戶打造一站式的分布式解決方案。
# AT 模式
## 前提
* 基于支持本地 ACID 事務的關系型數據庫。
* Java 應用,通過 JDBC 訪問數據庫。
## 整體機制
兩階段提交協議的演變:
* 一階段:業務數據和回滾日志記錄在同一個本地事務中提交,釋放本地鎖和連接資源。
* 二階段:
* 提交異步化,非常快速地完成。
* 回滾通過一階段的回滾日志進行反向補償。
# 寫隔離
* 一階段本地事務提交前,需要確保先拿到**全局鎖**。
* 拿不到**全局鎖**,不能提交本地事務。
* 拿**全局鎖**的嘗試被限制在一定范圍內,超出范圍將放棄,并回滾本地事務,釋放本地鎖。
以一個示例來說明:
兩個全局事務 tx1 和 tx2,分別對 a 表的 m 字段進行更新操作,m 的初始值 1000。
tx1 先開始,開啟本地事務,拿到本地鎖,更新操作 m = 1000 - 100 = 900。本地事務提交前,先拿到該記錄的**全局鎖**,本地提交釋放本地鎖。 tx2 后開始,開啟本地事務,拿到本地鎖,更新操作 m = 900 - 100 = 800。本地事務提交前,嘗試拿該記錄的**全局鎖**,tx1 全局提交前,該記錄的全局鎖被 tx1 持有,tx2 需要重試等待**全局鎖**。

tx1 二階段全局提交,釋放**全局鎖**。tx2 拿到**全局鎖**提交本地事務。

如果 tx1 的二階段全局回滾,則 tx1 需要重新獲取該數據的本地鎖,進行反向補償的更新操作,實現分支的回滾。
此時,如果 tx2 仍在等待該數據的**全局鎖**,同時持有本地鎖,則 tx1 的分支回滾會失敗。分支的回滾會一直重試,直到 tx2 的**全局鎖**等鎖超時,放棄**全局鎖**并回滾本地事務釋放本地鎖,tx1 的分支回滾最終成功。
因為整個過程**全局鎖**在 tx1 結束前一直是被 tx1 持有的,所以不會發生**臟寫**的問題。
# 讀隔離
在數據庫本地事務隔離級別**讀已提交(Read Committed)**或以上的基礎上,Seata(AT 模式)的默認全局隔離級別是**讀未提交(Read Uncommitted)**。
如果應用在特定場景下,必需要求全局的**讀已提交**,目前 Seata 的方式是通過 SELECT FOR UPDATE 語句的代理。

SELECT FOR UPDATE 語句的執行會申請**全局鎖**,如果**全局鎖**被其他事務持有,則釋放本地鎖(回滾 SELECT FOR UPDATE 語句的本地執行)并重試。這個過程中,查詢是被 block 住的,直到**全局鎖**拿到,即讀取的相關數據是**已提交**的,才返回。
出于總體性能上的考慮,Seata 目前的方案并沒有對所有 SELECT 語句都進行代理,僅針對 FOR UPDATE 的 SELECT 語句。
# 工作機制
以一個示例來說明整個 AT 分支的工作過程。
業務表:`product`
| Field | Type | Key |
| --- | --- | --- |
| id | bigint(20) | PRI |
| name | varchar(100) | |
| since | varchar(100) | |
AT 分支事務的業務邏輯:
~~~sql
update product set name = 'GTS' where name = 'TXC';
~~~
## 一階段
過程:
1. 解析 SQL:得到 SQL 的類型(UPDATE),表(product),條件(where name = 'TXC')等相關的信息。
2. 查詢前鏡像:根據解析得到的條件信息,生成查詢語句,定位數據。
~~~sql
select id, name, since from product where name = 'TXC';
~~~
得到前鏡像:
| id | name | since |
| --- | --- | --- |
| 1 | TXC | 2014 |
3. 執行業務 SQL:更新這條記錄的 name 為 'GTS'。
4. 查詢后鏡像:根據前鏡像的結果,通過**主鍵**定位數據。
~~~sql
select id, name, since from product where id = 1`;
~~~
得到后鏡像:
| id | name | since |
| --- | --- | --- |
| 1 | GTS | 2014 |
5. 插入回滾日志:把前后鏡像數據以及業務 SQL 相關的信息組成一條回滾日志記錄,插入到`UNDO_LOG`表中。
~~~json
{
"branchId": 641789253,
"undoItems": [{
"afterImage": {
"rows": [{
"fields": [{
"name": "id",
"type": 4,
"value": 1
}, {
"name": "name",
"type": 12,
"value": "GTS"
}, {
"name": "since",
"type": 12,
"value": "2014"
}]
}],
"tableName": "product"
},
"beforeImage": {
"rows": [{
"fields": [{
"name": "id",
"type": 4,
"value": 1
}, {
"name": "name",
"type": 12,
"value": "TXC"
}, {
"name": "since",
"type": 12,
"value": "2014"
}]
}],
"tableName": "product"
},
"sqlType": "UPDATE"
}],
"xid": "xid:xxx"
}
~~~
6. 提交前,向 TC 注冊分支:申請`product`表中,主鍵值等于 1 的記錄的**全局鎖**。
7. 本地事務提交:業務數據的更新和前面步驟中生成的 UNDO LOG 一并提交。
8. 將本地事務提交的結果上報給 TC。
## 二階段-回滾
1. 收到 TC 的分支回滾請求,開啟一個本地事務,執行如下操作。
2. 通過 XID 和 Branch ID 查找到相應的 UNDO LOG 記錄。
3. 數據校驗:拿 UNDO LOG 中的后鏡與當前數據進行比較,如果有不同,說明數據被當前全局事務之外的動作做了修改。這種情況,需要根據配置策略來做處理,詳細的說明在另外的文檔中介紹。
4. 根據 UNDO LOG 中的前鏡像和業務 SQL 的相關信息生成并執行回滾的語句:
~~~sql
update product set name = 'TXC' where id = 1;
~~~
5. 提交本地事務。并把本地事務的執行結果(即分支事務回滾的結果)上報給 TC。
## 二階段-提交
1. 收到 TC 的分支提交請求,把請求放入一個異步任務的隊列中,馬上返回提交成功的結果給 TC。
2. 異步任務階段的分支提交請求將異步和批量地刪除相應 UNDO LOG 記錄。
# 附錄
## 回滾日志表
UNDO\_LOG Table:不同數據庫在類型上會略有差別。
以 MySQL 為例:
| Field | Type |
| --- | --- |
| branch\_id | bigint PK |
| xid | varchar(100) |
| context | varchar(128) |
| rollback\_info | longblob |
| log\_status | tinyint |
| log\_created | datetime |
| log\_modified | datetime |
~~~sql
-- 注意此處0.7.0+ 增加字段 context
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
~~~
# TCC 模式
回顧總覽中的描述:一個分布式的全局事務,整體是**兩階段提交**的模型。全局事務是由若干分支事務組成的,分支事務要滿足**兩階段提交**的模型要求,即需要每個分支事務都具備自己的:
* 一階段 prepare 行為
* 二階段 commit 或 rollback 行為

根據兩階段行為模式的不同,我們將分支事務劃分為**Automatic (Branch) Transaction Mode**和**Manual (Branch) Transaction Mode**.
AT 模式([參考鏈接 TBD](https://seata.io/zh-cn/docs/overview/what-is-seata.html))基于**支持本地 ACID 事務**的**關系型數據庫**:
* 一階段 prepare 行為:在本地事務中,一并提交業務數據更新和相應回滾日志記錄。
* 二階段 commit 行為:馬上成功結束,**自動**異步批量清理回滾日志。
* 二階段 rollback 行為:通過回滾日志,**自動**生成補償操作,完成數據回滾。
相應的,TCC 模式,不依賴于底層數據資源的事務支持:
* 一階段 prepare 行為:調用**自定義**的 prepare 邏輯。
* 二階段 commit 行為:調用**自定義**的 commit 邏輯。
* 二階段 rollback 行為:調用**自定義**的 rollback 邏輯。
所謂 TCC 模式,是指支持把**自定義**的分支事務納入到全局事務的管理中。
# Saga 模式
Saga模式是SEATA提供的長事務解決方案,在Saga模式中,業務流程中每個參與者都提交本地事務,當出現某一個參與者失敗則補償前面已經成功的參與者,一階段正向服務和二階段補償服務都由業務開發實現。

理論基礎:Hector & Kenneth 發表論? Sagas (1987)
## 適用場景:
* 業務流程長、業務流程多
* 參與者包含其它公司或遺留系統服務,無法提供 TCC 模式要求的三個接口
## 優勢:
* 一階段提交本地事務,無鎖,高性能
* 事件驅動架構,參與者可異步執行,高吞吐
* 補償服務易于實現
## 缺點:
* 不保證隔離性(應對方案見[用戶文檔](https://seata.io/zh-cn/docs/user/saga.html))
- 第零章 序
- 序言
- 系統架構
- 視頻公開課
- 開源版介紹
- 商業版介紹
- 功能對比
- 答疑流程
- 第一章 快速開始
- 升級必看
- 環境要求
- 環境準備
- 基礎環境安裝
- Docker安裝基礎服務
- Nacos安裝
- Sentinel安裝
- 插件安裝
- 建數據庫
- 工程導入
- 導入Cloud版本
- 導入Nacos配置
- 導入Boot版本
- 工程運行
- 運行Cloud版本
- 運行Boot版本
- 工程測試
- 測試Cloud版本
- 測試Boot版本
- 第二章 技術基礎
- Java
- Lambda
- Lambda 受檢異常處理
- Stream 簡介
- Stream API 一覽
- Stream API (上)
- Stream API (下)
- Optional 干掉空指針
- 函數式接口
- 新的日期 API
- Lombok
- SpringMVC
- Swagger
- Mybatis
- Mybatis-Plus
- 開發規范
- 第三章 開發初探
- 新建微服務工程
- 第一個API
- API鑒權
- API響應結果
- Redis緩存
- 第一個CRUD
- 建表
- 建Entity
- 建Service和Mapper
- 新增 API
- 修改 API
- 刪除 API
- 查詢 API
- 單條數據
- 多條數據
- 分頁
- 微服務遠程調用
- 聲明式服務調用 Feign
- 熔斷機制 Hystrix
- 第四章 開發進階
- 聚合文檔
- 鑒權配置
- 跨域處理
- Xss防注入
- 自定義啟動器
- Secure安全框架
- Token認證簡介
- Token認證配置
- PreAuth注解配置
- Token認證實戰
- Token認證加密
- 日志系統
- 原理解析
- 功能調用
- Seata分布式事務
- 簡介
- 編譯包啟動
- 配置nacos對接
- docker啟動
- 對接微服務
- 代碼生成配置
- 前言
- 數據庫建表
- 代碼生成
- 前端配置
- 優化效果
- 第五章 功能特性
- SaaS多租戶
- 概念
- 數據隔離配置
- 線程環境自定義租戶ID
- 多終端令牌認證
- 概念
- 系統升級
- 使用
- 第三方系統登錄
- 概念說明
- 對接說明
- 對接準備
- 配置說明
- 操作流程
- 后記
- UReport2報表
- 報表簡介
- 對接配置
- 報表后記
- 接口報文加密
- 簡介
- 運行邏輯
- 對接準備
- 功能配置
- 接口測試
- 改造查詢
- 改造提交
- 改造刪除
- 動態數據權限
- 數據權限簡介
- 數據權限開發
- 純注解配置
- Web全自動配置
- 注解半自動配置
- 數據權限注意點
- 動態接口權限
- 樂觀鎖配置
- 統一服務登陸配置
- Skywalking追蹤監控
- Minio分布式對象存儲
- Boot版本對接至Cloud
- 第六章 生產部署
- windows部署
- linux部署
- jar部署
- docker部署
- java環境安裝
- mysql安裝
- docker安裝
- docker-compose安裝
- harbor安裝
- 部署步驟
- 寶塔部署
- 準備工作
- 安裝工作
- 部署準備
- 部署后端
- 部署前端
- 部署域名
- 結束工作
- k8s平臺部署
- 第七章 版本控制
- Git遠程分支合并
- Git地址更換
- 第八章 學習資料
- 第九章 FAQ
- 第十章 聯系我們