Seata之分布式事務(wù)問(wèn)題及解決方案
Seata–分布式事務(wù)解決方案
簡(jiǎn)介
Seata 是阿里開(kāi)源的分布式事務(wù)解決方案,提供 AT、XA、TCC、Saga 四種事務(wù)模式,支持微服務(wù)架構(gòu)下的數(shù)據(jù)一致性。
官網(wǎng):https://seata.apache.org/zh-cn/
同類產(chǎn)品對(duì)比
| 方案 | 核心特點(diǎn) | 適用場(chǎng)景 |
|---|---|---|
| Seata | 多模式支持,代碼侵入性低,社區(qū)活躍 | 復(fù)雜業(yè)務(wù)場(chǎng)景,需靈活選擇模式 |
| 阿里云 GTS | 商業(yè)版方案,功能全面,性能強(qiáng) | 企業(yè)級(jí)付費(fèi)場(chǎng)景 |
| RocketMQ 事務(wù)消息 | 基于消息隊(duì)列實(shí)現(xiàn)最終一致性 | 異步高吞吐場(chǎng)景 |
| LCN | 基于代理模式,實(shí)現(xiàn)簡(jiǎn)單 | 輕量級(jí)快速接入 |
環(huán)境搭建
1.微服務(wù)
創(chuàng)建 Spring Cloud 項(xiàng)目,推薦使用以下組件:
<!-- Spring Cloud Alibaba 依賴 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>2.SQL
每個(gè)微服務(wù)數(shù)據(jù)庫(kù)需創(chuàng)建 undo_log 表(AT模式必需):
-- 每個(gè)業(yè)務(wù)數(shù)據(jù)庫(kù)均需執(zhí)行
CREATE TABLE IF NOT EXISTS `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;3.seata-server
- 1.下載 :https://seata.apache.org/zh-cn/download/seata-server
- 2.解壓并啟動(dòng):seata-server.bat
- 3.控制臺(tái):http://127.0.0.1:7091/#/transaction/list
4.微服務(wù)配置
依賴
除基礎(chǔ)依賴外需添加:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency>
配置
每個(gè)微服務(wù)創(chuàng)建 file.conf文件,完整內(nèi)容如下;
【微服務(wù)只需要復(fù)制 service 塊配置即可】
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
transport {
# tcp, unix-domain-socket
type = "TCP"
#NIO, NATIVE
server = "NIO"
#enable heartbeat
heartbeat = true
# the tm client batch send request enable
enableTmClientBatchSendRequest = false
# the rm client batch send request enable
enableRmClientBatchSendRequest = true
# the rm client rpc request timeout
rpcRmRequestTimeout = 2000
# the tm client rpc request timeout
rpcTmRequestTimeout = 30000
# the rm client rpc request timeout
rpcRmRequestTimeout = 15000
#thread factory for netty
threadFactory {
bossThreadPrefix = "NettyBoss"
workerThreadPrefix = "NettyServerNIOWorker"
serverExecutorThread-prefix = "NettyServerBizHandler"
shareBossWorker = false
clientSelectorThreadPrefix = "NettyClientSelector"
clientSelectorThreadSize = 1
clientWorkerThreadPrefix = "NettyClientWorkerThread"
# netty boss thread size
bossThreadSize = 1
#auto default pin or 8
workerThreadSize = "default"
}
shutdown {
# when destroy server, wait seconds
wait = 3
}
serialization = "seata"
compressor = "none"
}
service {
#transaction service group mapping
vgroupMapping.default_tx_group = "default"
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "127.0.0.1:8091"
#degrade, current not support
enableDegrade = false
#disable seata
disableGlobalTransaction = false
}
client {
rm {
asyncCommitBufferLimit = 10000
lock {
retryInterval = 10
retryTimes = 30
retryPolicyBranchRollbackOnConflict = true
}
reportRetryCount = 5
tableMetaCheckEnable = false
tableMetaCheckerInterval = 60000
reportSuccessEnable = false
sagaBranchRegisterEnable = false
sagaJsonParser = "fastjson"
sagaRetryPersistModeUpdate = false
sagaCompensatePersistModeUpdate = false
tccActionInterceptorOrder = -2147482648 #Ordered.HIGHEST_PRECEDENCE + 1000
sqlParserType = "druid"
branchExecutionTimeoutXA = 60000
connectionTwoPhaseHoldTimeoutXA = 10000
}
tm {
commitRetryCount = 5
rollbackRetryCount = 5
defaultGlobalTransactionTimeout = 60000
degradeCheck = false
degradeCheckPeriod = 2000
degradeCheckAllowTimes = 10
interceptorOrder = -2147482648 #Ordered.HIGHEST_PRECEDENCE + 1000
}
undo {
dataValidation = true
onlyCareUpdateColumns = true
logSerialization = "jackson"
logTable = "undo_log"
compress {
enable = true
# allow zip, gzip, deflater, lz4, bzip2, zstd default is zip
type = zip
# if rollback info size > threshold, then will be compress
# allow k m g t
threshold = 64k
}
}
loadBalance {
type = "XID"
virtualNodes = 10
}
}
log {
exceptionRate = 100
}
tcc {
fence {
# tcc fence log table name
logTableName = tcc_fence_log
# tcc fence log clean period
cleanPeriod = 1h
}
}事務(wù)模式
1.AT模式(推薦:自動(dòng)補(bǔ)償)
二階提交協(xié)議原理
原理:基于反向SQL補(bǔ)償,自動(dòng)生成回滾日志

使用:
@Service
public class OrderService {
@GlobalTransactional(name = "createOrder", timeoutMills = 60000)
public void createOrder(OrderDTO order) {
// 1. 扣減庫(kù)存(調(diào)用庫(kù)存服務(wù))
storageFeignClient.deduct(order.getProductId());
// 2. 創(chuàng)建訂單(本地事務(wù))
orderMapper.insert(order);
// 3. 模擬異常觸發(fā)回滾
int i = 1 / 0;
}
}關(guān)鍵機(jī)制:
- 一階段:提交本地事務(wù),生成回滾日志(undo_log)
- 二階段:成功則異步刪除日志,失敗則通過(guò)日志反向補(bǔ)償
特點(diǎn):
- 對(duì)代碼無(wú)侵入
- 需創(chuàng)建undo_log表
- 適用于大多數(shù)CRUD場(chǎng)景
2.XA模式(強(qiáng)一致)
原理:基于數(shù)據(jù)庫(kù)XA協(xié)議的兩階段提交
配置:
seata: data-source-proxy-mode: XA # 默認(rèn)AT
特點(diǎn):
- 基于數(shù)據(jù)庫(kù)XA協(xié)議
- 兩階段提交(2PC)
- 事務(wù)持有鎖時(shí)間較長(zhǎng),適合短事務(wù)
適用場(chǎng)景:強(qiáng)一致性需求,支持XA協(xié)議的數(shù)據(jù)庫(kù)(如MySQL 5.7+)
3.TCC模式(手動(dòng)補(bǔ)償)
原理:Try-Confirm-Cancel 三階段控制
實(shí)現(xiàn):
// 1. 定義TCC接口
public interface StorageTccService {
@TwoPhaseBusinessAction(name = "deduct",
commitMethod = "confirm",
rollbackMethod = "cancel")
boolean deduct(@BusinessActionContextParameter(paramName = "productId") String productId,
@BusinessActionContextParameter(paramName = "count") Integer count);
boolean confirm(BusinessActionContext context);
boolean cancel(BusinessActionContext context);
}
// 2. 實(shí)現(xiàn)Try邏輯
@Service
public class StorageTccServiceImpl implements StorageTccService {
@Override
public boolean deduct(String productId, Integer count) {
// Try階段:資源預(yù)留(例如凍結(jié)庫(kù)存)
return storageMapper.freezeStock(productId, count) > 0;
}
@Override
public boolean confirm(BusinessActionContext context) {
// Confirm階段:真實(shí)扣減(例如刪除凍結(jié)記錄)
String productId = context.getActionContext("productId");
Integer count = context.getActionContext("count");
return storageMapper.reduceStock(productId, count) > 0;
}
@Override
public boolean cancel(BusinessActionContext context) {
// Cancel階段:釋放資源(例如恢復(fù)凍結(jié)庫(kù)存)
String productId = context.getActionContext("productId");
Integer count = context.getActionContext("count");
return storageMapper.unfreezeStock(productId, count) > 0;
}
}使用限制:
- 需自行實(shí)現(xiàn)Try/Confirm/Cancel方法
- Confirm和Cancel需保證冪等性
特點(diǎn):高性能,但需手動(dòng)編寫(xiě)補(bǔ)償邏輯
4.Saga模式(長(zhǎng)事務(wù))
實(shí)現(xiàn)方式:
通過(guò)狀態(tài)機(jī)配置補(bǔ)償策略:
@SagaStart
public void createOrderSaga(Order order) {
// 1. 創(chuàng)建訂單
orderService.create(order);
// 2. 調(diào)用支付服務(wù)(若失敗則觸發(fā)逆向操作)
paymentService.pay(order.getId());
}
// 定義補(bǔ)償方法
@Compensate
public void compensateOrder(Order order) {
orderService.delete(order.getId());
}原理:長(zhǎng)事務(wù)拆分+逆向補(bǔ)償
適用場(chǎng)景:跨系統(tǒng)長(zhǎng)時(shí)間操作(如訂單+物流+支付)
總結(jié)
模式選型對(duì)照表:
| 模式 | 一致性 | 性能 | 侵入性 | 適用場(chǎng)景 |
|---|---|---|---|---|
| AT | 弱一致 | 高 | 低 | 常規(guī)業(yè)務(wù)(庫(kù)存扣減、訂單創(chuàng)建) |
| TCC | 強(qiáng)一致 | 中 | 高 | 資金交易、需精準(zhǔn)控制 |
| XA | 強(qiáng)一致 | 低 | 低 | 銀行轉(zhuǎn)賬、短事務(wù) |
| Saga | 最終一致 | 高 | 中 | 跨系統(tǒng)長(zhǎng)流程(訂單+物流+支付) |
核心要點(diǎn):
| 分類 | 要點(diǎn)說(shuō)明 |
|---|---|
| 選型 | AT模式適用于大多數(shù)場(chǎng)景,TCC適合高性能要求,XA適合強(qiáng)一致性 |
| 配置 | 確保seata-server與微服務(wù)的registry配置一致 |
| 事務(wù)ID | 通過(guò)RootContext.getXID()可獲取當(dāng)前事務(wù)ID |
| 排錯(cuò) | 檢查undo_log表記錄,查看seata-server控制臺(tái)日志 |
最佳實(shí)踐:
- 生產(chǎn)環(huán)境建議使用Nacos作為配置中心
- AT模式需要開(kāi)啟數(shù)據(jù)庫(kù)的本地事務(wù)支持(如MySQL的InnoDB引擎)
- 全局事務(wù)超時(shí)時(shí)間建議設(shè)置:
seata.tx.timeout=60000(單位毫秒)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
postman?如何實(shí)現(xiàn)傳遞?ArrayList?給后臺(tái)
這篇文章主要介紹了postman?如何實(shí)現(xiàn)傳遞?ArrayList給后臺(tái),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
解決@SpringBootTest 單元測(cè)試遇到的坑
這篇文章主要介紹了解決@SpringBootTest 單元測(cè)試遇到的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
Java 8系列之Stream中萬(wàn)能的reduce用法說(shuō)明
這篇文章主要介紹了Java 8系列之Stream中萬(wàn)能的reduce用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08
關(guān)于Java并發(fā)編程中線程間協(xié)作的兩種方式
這篇文章主要介紹了關(guān)于Java并發(fā)編程中線程間協(xié)作的兩種方式,當(dāng)隊(duì)列滿時(shí),生產(chǎn)者需要等待隊(duì)列有空間才能繼續(xù)往里面放入商品,而在等待的期間內(nèi),生產(chǎn)者必須釋放對(duì)臨界資源的占用權(quán),這是消費(fèi)者模式,需要的朋友可以參考下2023-07-07
Spring Cloud Gateway 內(nèi)存溢出的解決方案
這篇文章主要介紹了Spring Cloud Gateway 內(nèi)存溢出的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Spring-Cloud Eureka注冊(cè)中心實(shí)現(xiàn)高可用搭建
這篇文章主要介紹了Spring-Cloud Eureka注冊(cè)中心實(shí)現(xiàn)高可用搭建,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
Java自定義實(shí)現(xiàn)鏈隊(duì)列詳解
這篇文章主要為大家詳細(xì)介紹了Java自定義實(shí)現(xiàn)鏈隊(duì)列的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12

