# Seata如何搭建與分布式事務入門
## 目錄
1. [分布式事務基礎概念](#一分布式事務基礎概念)
- 1.1 [什么是分布式事務](#11-什么是分布式事務)
- 1.2 [CAP理論與BASE理論](#12-cap理論與base理論)
- 1.3 [常見解決方案對比](#13-常見解決方案對比)
2. [Seata核心架構解析](#二seata核心架構解析)
- 2.1 [TC/TA/RM角色劃分](#21-tctarm角色劃分)
- 2.2 [AT模式原理詳解](#22-at模式原理詳解)
- 2.3 [TCC/SAGA模式對比](#23-tccsaga模式對比)
3. [Seata Server部署實戰](#三seata-server部署實戰)
- 3.1 [單機版部署指南](#31-單機版部署指南)
- 3.2 [高可用集群配置](#32-高可用集群配置)
- 3.3 [注冊中心與配置中心](#33-注冊中心與配置中心)
4. [Spring Cloud集成實踐](#四spring-cloud集成實踐)
- 4.1 [Spring Boot Starter配置](#41-spring-boot-starter配置)
- 4.2 [數據源代理配置](#42-數據源代理配置)
- 4.3 [全局事務注解使用](#43-全局事務注解使用)
5. [生產環境優化建議](#五生產環境優化建議)
- 5.1 [參數調優指南](#51-參數調優指南)
- 5.2 [常見問題排查](#52-常見問題排查)
- 5.3 [監控與告警配置](#53-監控與告警配置)
6. [實戰案例演示](#六實戰案例演示)
- 6.1 [電商下單場景](#61-電商下單場景)
- 6.2 [跨行轉賬場景](#62-跨行轉賬場景)
## 一、分布式事務基礎概念
### 1.1 什么是分布式事務
在單體應用架構中,我們通常使用本地事務(如MySQL的InnoDB引擎事務)來保證數據一致性。但當系統演進為微服務架構時,一個業務操作往往需要跨多個服務、多個數據庫,這就產生了分布式事務需求。
**典型場景示例**:
```java
// 偽代碼展示分布式事務場景
@DistributedTransactional
public void createOrder(OrderDTO order) {
// 操作1:庫存服務扣減庫存
inventoryService.reduceStock(order.getItems());
// 操作2:訂單服務創建訂單
orderService.create(order);
// 操作3:賬戶服務扣減余額
accountService.debit(order.getUserId(), order.getAmount());
}
分布式系統必須滿足P,因此在C和A之間需要權衡。Seata采用的AT模式優先保證AP,最終達到C。
| 方案 | 原理 | 優點 | 缺點 |
|---|---|---|---|
| 2PC | 兩階段提交 | 強一致性 | 同步阻塞、單點問題 |
| TCC | Try-Confirm-Cancel | 高靈活性 | 開發復雜度高 |
| SAGA | 長事務拆分 | 適合長流程 | 無隔離性 |
| 本地消息表 | 異步確保型 | 簡單可靠 | 時效性差 |
| Seata AT | 反向SQL補償 | 零侵入、高性能 | 全局鎖競爭 |

階段一:業務數據+回滾日志入庫
-- 原始SQL
UPDATE product SET stock = stock - 1 WHERE id = 1001;
-- Seata代理后實際執行
-- 1. 查詢前鏡像
SELECT stock FROM product WHERE id = 1001;
-- 2. 執行業務SQL
UPDATE product SET stock = stock - 1 WHERE id = 1001;
-- 3. 查詢后鏡像
SELECT stock FROM product WHERE id = 1001;
-- 4. 插入undo_log
INSERT INTO undo_log VALUES(...);
階段二提交流程: 1. TM向TC發起全局提交 2. TC異步刪除各分支undo_log
階段二回滾流程: 1. TM向TC發起全局回滾 2. TC查詢undo_log構造補償SQL
UPDATE product SET stock = stock + 1 WHERE id = 1001;
public interface AccountService {
@TwoPhaseBusinessAction(name = "debit", commitMethod = "confirm", rollbackMethod = "cancel")
boolean tryDebit(@BusinessActionContextParameter(paramName = "userId") String userId,
@BusinessActionContextParameter(paramName = "money") BigDecimal money);
boolean confirm(BusinessActionContext context);
boolean cancel(BusinessActionContext context);
}
步驟1:下載安裝包
wget https://github.com/seata/seata/releases/download/v1.5.2/seata-server-1.5.2.tar.gz
tar -zxvf seata-server-1.5.2.tar.gz
步驟2:修改conf/registry.conf
registry {
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = ""
cluster = "default"
}
}
步驟3:啟動服務
sh bin/seata-server.sh -p 8091 -h 127.0.0.1
數據庫存儲模式配置(conf/file.conf):
store {
mode = "db"
db {
datasource = "druid"
dbType = "mysql"
url = "jdbc:mysql://127.0.0.1:3306/seata"
user = "root"
password = "123456"
minConn = 5
maxConn = 30
}
}
需要初始化的數據庫表:
-- 全局事務表
CREATE TABLE IF NOT EXISTS global_table (
xid VARCHAR(128) NOT NULL PRIMARY KEY,
...
);
-- 分支事務表
CREATE TABLE IF NOT EXISTS branch_table (
branch_id BIGINT NOT NULL PRIMARY KEY,
xid VARCHAR(128) NOT NULL,
...
);
Nacos配置中心同步: 1. 將config.txt上傳到Nacos
python3 nacos-config.py -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t 5a3c7d6c-f497-4d68-a71a-2e5e1230
pom.xml依賴:
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
application.yml配置:
seata:
application-id: order-service
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: default
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
必須配置項:
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
@Primary
@Bean("dataSource")
public DataSource dataSource(DruidDataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
}
基礎用法:
@GlobalTransactional(timeoutMills = 60000, name = "createOrder")
public void createOrder(Order order) {
// 1. 扣減庫存
storageFeignService.deduct(order.getCommodityCode(), order.getCount());
// 2. 創建訂單
orderMapper.insert(order);
// 3. 扣減余額
accountFeignService.debit(order.getUserId(), order.getMoney());
}
注意事項: 1. 避免在事務方法內處理HTTP請求 2. 超時時間要大于所有分支事務耗時總和 3. 建議在入口Controller方法上聲明
關鍵server端參數(seata-server/conf/file.conf):
transport {
thread-factory {
boss-thread-prefix = "NettyBoss"
worker-thread-prefix = "NettyServerNIOWorker"
server-executor-thread-prefix = "NettyServerBizHandler"
share-boss-worker = false
client-selector-thread-prefix = "NettyClientSelector"
client-selector-thread-size = 1
client-worker-thread-prefix = "NettyClientWorkerThread"
worker-thread-size = "default"
}
}
客戶端優化建議:
seata:
client:
rm-report-success-enable: false # 關閉分支執行結果上報
report-retry-count: 5 # 重試次數
全局鎖沖突處理: 1. 檢查業務SQL條件是否走索引 2. 減少單個事務涉及的數據范圍 3. 適當調整隔離級別:
@GlobalTransactional(lockRetryInternal = 100, lockRetryTimes = 30)
事務懸掛問題: - 現象:分支事務先于全局事務注冊 - 解決方案:升級到1.5.0+版本,開啟:
seata:
client:
undo:
data-validation: true
Prometheus監控配置:
metrics {
enabled = true
registry-type = "compact"
exporter-list = "prometheus"
exporter-prometheus-port = 9898
}
Grafana監控看板: 1. 導入官方Dashboard(ID:10477) 2. 關鍵監控指標: - seata.transaction.active.count - seata.transaction.committed.rate - seata.transaction.rollback.rate
業務流程圖:
sequenceDiagram
participant C as Client
participant O as OrderService
participant S as StorageService
participant A as AccountService
C->>O: 提交訂單
O->>S: 扣減庫存
S-->>O: 操作成功
O->>A: 扣減余額
A-->>O: 操作成功
O->>O: 生成訂單
O-->>C: 下單成功
異常處理設計: 1. 庫存不足:立即返回錯誤 2. 余額不足:觸發全局回滾 3. 網絡超時:依賴重試機制
TCC模式實現:
// 轉賬服務接口
public interface TransferService {
@TwoPhaseBusinessAction(name = "transfer", commitMethod = "confirm", rollbackMethod = "cancel")
boolean tryTransfer(@BusinessActionContextParameter(paramName = "from") String from,
@BusinessActionContextParameter(paramName = "to") String to,
@BusinessActionContextParameter(paramName = "amount") BigDecimal amount);
}
// 賬戶服務實現
@Service
public class AccountServiceImpl implements AccountService {
@Transactional
public boolean tryDebit(String accountNo, BigDecimal amount) {
// 檢查賬戶狀態
// 凍結部分金額
accountMapper.freezeAmount(accountNo, amount);
return true;
}
public boolean confirm(BusinessActionContext context) {
// 實際扣減凍結金額
String accountNo = (String)context.getActionContext("from");
BigDecimal amount = (BigDecimal)context.getActionContext("amount");
accountMapper.debit(accountNo, amount);
accountMapper.unfreezeAmount(accountNo, amount);
return true;
}
}
SAGA模式設計:
// 狀態機定義示例
{
"name": "bankTransferSaga",
"steps": [
{
"name": "deductFromAccount",
"compensate": "compensateDeduct",
"service": "accountService",
"input": ["$.['from']", "$.['amount']"]
},
{
"name": "addToAccount",
"compensate": "compensateAdd",
"service": "accountService",
"input": ["$.['to']", "$.['amount']"]
}
]
}
作者建議:在實際項目中使用Seata時,建議從AT模式開始,隨著業務復雜度提升逐步考慮TCC或SAGA模式。同時要特別注意undo_log表的大小控制,建議定期歸檔歷史數據。 “`
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。