在微服务架构中,一个业务操作往往需要跨多个服务完成数据更新,这就产生了分布式事务问题。想象一下电商系统中的下单场景:订单服务创建订单、库存服务扣减库存、账户服务扣减余额——这三个操作要么全部成功,要么全部回滚。传统单机数据库的ACID事务在分布式环境下不再适用,这就是我们需要分布式事务解决方案的根本原因。
Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的分布式事务解决方案,其AT(Auto Transaction)模式通过代理数据源的方式,实现了对业务无侵入的分布式事务支持。与TCC、SAGA等模式相比,AT模式最大的优势在于开发者几乎不需要修改业务代码,只需添加注解即可获得分布式事务能力。
Seata AT模式的运作可以分为三个关键阶段:
一阶段准备:
二阶段提交:
二阶段回滚:
Seata架构包含三个核心组件:
典型交互流程如下:
推荐使用Docker快速部署Seata Server:
bash复制docker run --name seata-server \
-p 8091:8091 \
-e SEATA_IP=your_server_ip \
-e SEATA_PORT=8091 \
-v /path/to/registry.conf:/seata-server/resources/registry.conf \
seataio/seata-server:1.5.2
关键配置项说明(registry.conf):
conf复制registry {
type = "nacos" # 支持nacos、eureka等多种注册中心
nacos {
serverAddr = "nacos:8848"
namespace = ""
cluster = "default"
}
}
config {
type = "nacos"
nacos {
serverAddr = "nacos:8848"
namespace = ""
group = "SEATA_GROUP"
}
}
Spring Boot项目集成步骤:
xml复制<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.2</version>
</dependency>
yaml复制seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: default
registry:
type: nacos
nacos:
server-addr: nacos:8848
namespace: ""
group: "SEATA_GROUP"
java复制@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注解:
java复制@Service
public class OrderServiceImpl implements OrderService {
@GlobalTransactional(name = "createOrder", timeoutMills = 60000)
public void createOrder(OrderDTO orderDTO) {
// 1. 创建订单
orderMapper.create(orderDTO);
// 2. 扣减库存
storageFeignClient.deduct(orderDTO.getCommodityCode(), orderDTO.getCount());
// 3. 扣减余额
accountFeignClient.debit(orderDTO.getUserId(), orderDTO.getMoney());
}
}
分支服务方法只需添加@Transactional注解(注意:必须使用代理过的数据源):
java复制@Service
public class AccountServiceImpl implements AccountService {
@Transactional
public void debit(String userId, BigDecimal money) {
// 检查余额
Account account = accountMapper.selectByUserId(userId);
if(account.getBalance().compareTo(money) < 0) {
throw new RuntimeException("余额不足");
}
// 更新余额
accountMapper.updateBalance(userId, money.negate());
}
}
TC集群部署:
客户端重试策略:
yaml复制seata:
client:
rm:
report-retry-count: 5
table-meta-check-enable: false
tm:
commit-retry-count: 3
rollback-retry-count: 3
sql复制ALTER TABLE undo_log
ADD INDEX idx_xid (xid),
ADD INDEX idx_branch_id (branch_id);
conf复制# seata-server配置
transport.thread-factory.boss-thread-prefix=netty-boss
transport.thread-factory.worker-thread-prefix=netty-worker
server.recovery.committing-retry-period=1000
server.recovery.asyn-committing-retry-period=1000
server.recovery.rollbacking-retry-period=1000
server.recovery.timeout-retry-period=1000
检查XID是否传递:
数据源代理验证:
注册中心检查:
bash复制curl http://nacos:8848/nacos/v1/ns/instance/list?serviceName=serverAddr
问题1:Could not register branch into global session xid:xxx status:Timeout
解决方案:
yaml复制seata:
client:
rm:
async-commit-buffer-limit: 10000
report-success-enable: false
lock:
retry-interval: 10
retry-times: 30
问题2:UndoLog table not found
解决方案:
sql复制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;
对于涉及大量数据更新的分布式事务,建议:
java复制@GlobalTransactional
public void batchProcess(List<Item> items) {
// 每100条记录作为一个批次
Lists.partition(items, 100).forEach(batch -> {
processBatch(batch);
});
}
@Transactional
public void processBatch(List<Item> batch) {
// 处理单个批次
}
java复制@GlobalTransactional
public void asyncOperation() {
// 1. 创建预操作记录
long recordId = prepareRecord();
// 2. 异步执行(不在事务内)
CompletableFuture.runAsync(() -> {
try {
actualOperation(recordId);
} catch(Exception e) {
// 记录失败状态
markAsFailed(recordId);
}
});
}
对于特别复杂的场景,可以结合AT与TCC模式:
java复制@GlobalTransactional
public void hybridTransaction() {
// AT模式操作
orderService.create(order);
// TCC模式操作
couponService.prepare(null);
}
java复制public interface CouponService {
@TwoPhaseBusinessAction(name = "prepare", commitMethod = "commit", rollbackMethod = "rollback")
boolean prepare(BusinessActionContext context);
boolean commit(BusinessActionContext context);
boolean rollback(BusinessActionContext context);
}
在实际项目落地过程中,我们发现Seata AT模式最适合80%以上的常规分布式事务场景。对于特别复杂的业务逻辑或性能要求极高的场景,可以考虑结合TCC或SAGA模式实现。一个实用的建议是:先使用AT模式快速实现,遇到确实无法满足需求的场景再考虑其他方案,这样可以大幅降低开发复杂度。