微服务为什么需要分布式事务
单体应用拆分为微服务后,原本在同一个数据库事务中完成的操作分散到了多个服务中。比如电商下单场景:库存服务扣减库存、订单服务创建订单、账户服务扣减余额,这三个操作要么全部成功要么全部回滚。本地事务无法跨服务生效,分布式事务就是解决这个一致性问题的核心机制。
Seata的AT模式原理
Seata的AT(Automatic Transaction)模式是对业务代码侵入最小的分布式事务方案。其核心流程分为两阶段:
一阶段:拦截业务SQL,在执行前后记录数据快照(before-image和after-image),生成回滚日志(undo_log),一起提交到本地事务中。业务SQL和undo_log在同一个本地事务中原子提交。
二阶段提交:全局事务协调器通知各分支提交,异步删除undo_log即可(因为一阶段已经提交)。
二阶段回滚:读取undo_log中的before-image,反向补偿SQL恢复原始数据。
AT模式的优势在于业务代码零侵入——只需加一个@GlobalTransactional注解。
Seata Server部署
Seata Server(TC事务协调器)是独立的服务端组件,推荐使用Nacos作为注册中心和配置中心:
# 下载Seata Server 1.8+
wget https://github.com/seata/seata/releases/download/v1.8.0/seata-server-1.8.0.tar.gz
tar -xzf seata-server-1.8.0.tar.gz
cd seata
配置conf/application.yml:
server:
port: 7091
seata:
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: seata
group: SEATA_GROUP
data-id: seataServer.properties
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
namespace: seata
group: SEATA_GROUP
store:
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true
user: seata
password: seata_pwd
在Nacos配置中心创建seataServer.properties,包含事务分组映射:
# 事务分组映射
service.vgroupMapping.order-service-group=default
service.vgroupMapping.inventory-service-group=default
service.vgroupMapping.account-service-group=default
# 锁和会话存储模式
store.lock.mode=db
store.session.mode=db
Spring Boot微服务集成Seata
每个参与分布式事务的微服务都需要集成Seata Client。以订单服务为例:
1. 添加Maven依赖
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2022.0.0.0</version>
</dependency>
2. 配置application.yml
seata:
enabled: true
application-id: order-service
tx-service-group: order-service-group
service:
vgroup-mapping:
order-service-group: default
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: seata
group: SEATA_GROUP
application: seata-server
3. 创建undo_log表
每个参与分布式事务的数据库都需要创建undo_log表:
CREATE TABLE IF NOT EXISTS `undo_log` (
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`context` VARCHAR(128) NOT NULL,
`rollback_info` LONGBLOB NOT NULL,
`log_status` INT(11) NOT NULL,
`log_created` DATETIME(6) NOT NULL,
`log_modified` DATETIME(6) NOT NULL,
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
业务代码中使用@GlobalTransactional
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private InventoryClient inventoryClient;
@Autowired
private AccountClient accountClient;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public Order createOrder(OrderDTO orderDTO) {
// 1. 创建订单(本地事务)
Order order = new Order();
order.setUserId(orderDTO.getUserId());
order.setProductId(orderDTO.getProductId());
order.setCount(orderDTO.getCount());
order.setAmount(orderDTO.getAmount());
order.setStatus("CREATING");
orderMapper.insert(order);
// 2. 远程调用:扣减库存
inventoryClient.deduct(orderDTO.getProductId(), orderDTO.getCount());
// 3. 远程调用:扣减余额
accountClient.debit(orderDTO.getUserId(), orderDTO.getAmount());
// 4. 更新订单状态
order.setStatus("COMPLETED");
orderMapper.updateById(order);
return order;
}
}
只需在方法上加@GlobalTransactional注解,Seata会自动协调三个服务的本地事务。任何一个服务抛出异常,所有已提交的分支事务会自动回滚。
Feign拦截器传递XID
微服务间通过Feign调用时,需要将全局事务XID传递到下游服务。添加Feign拦截器:
@Component
public class SeataFeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
String xid = RootContext.getXID();
if (StringUtils.isNotBlank(xid)) {
template.header(RootContext.KEY_XID, xid);
}
}
}
// 同时需要配置Seata的拦截器,在接收端恢复XID
@Configuration
public class SeataFilterConfig {
@Bean
public SeataFilter seataFilter() {
return new SeataFilter();
}
}
常见问题与排障
问题1:全局事务超时
默认超时时间60秒。如果业务逻辑确实需要更长时间:
@GlobalTransactional(timeoutMills = 300000) // 5分钟
问题2:undo_log找不到导致回滚失败
undo_log被手动清理或数据库异常丢数据。检查undo_log表数据完整性,必要时手动清理脏数据并重新初始化。
问题3:全局锁冲突
并发场景下多个全局事务争抢同一行数据的全局锁。优化方式:减少全局事务持有锁的时间,将非关键操作移到事务外;或者使用GlobalLock注解在本地事务中获取全局锁。
生产环境注意事项
1. Seata Server必须集群部署(至少2个节点),避免TC单点故障。
2. 使用数据库存储模式(store.mode=db),不要用file模式,file模式不支持集群。
3. 监控Seata事务指标:事务成功率、平均耗时、回滚率。回滚率超过10%需要排查业务逻辑。
4. 对非核心链路考虑使用最终一致性方案(消息表+定时补偿),降低Seata的负载。
5. 做好幂等设计。分布式事务重试可能导致同一操作执行多次,下游接口必须保证幂等性。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-wei-fu-wu-jia-gou-zhong-seata-fen-bu-shi-shi-wu/