微服务拆分后的事务一致性挑战
单体应用拆分为微服务后,原本一个数据库事务就能搞定的业务操作,现在跨越多个服务,每个服务独占自己的数据库实例。下单流程就是典型案例:订单服务创建订单、库存服务扣减库存、支付服务冻结资金——三步操作分布在三个独立的数据库中,任何一步失败都需要全部回滚。
分布式事务的核心矛盾是:CAP定理下,高可用与强一致性无法同时保证。工程实践中选择哪种方案,取决于业务对一致性等级的要求:
// 一致性等级与适用场景
强一致性(2PC/XA) → 资金交易、库存扣减
最终一致性(TCC/Saga)→ 订单流程、业务中台
弱一致性(最大努力通知)→ 日志采集、通知推送
Seata AT模式:低侵入式分布式事务
Seata的AT模式(Auto Transaction)是Java生态中最落地的方案,对业务代码侵入最小——只需要在发起方方法上加一个@GlobalTransactional注解。
// Seata AT模式配置
// 1. 引入依赖(Spring Boot 3.2+ / JDK 17)
// pom.xml
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.8.0</version>
</dependency>
// 2. 配置Seata数据源代理
@Configuration
public class SeataDataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public DataSource dataSource() {
return new HikariDataSource();
}
@Bean
@Primary
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
}
// 3. 业务代码 - 订单服务(事务发起方)
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private InventoryClient inventoryClient;
@Autowired
private PaymentClient paymentClient;
@GlobalTransactional(timeoutMills = 30000, name = "create-order")
public OrderResult createOrder(CreateOrderRequest request) {
// 1. 创建订单(本地事务,Seata自动生成undo_log)
Order order = new Order();
order.setUserId(request.getUserId());
order.setProductId(request.getProductId());
order.setQuantity(request.getQuantity());
order.setStatus(OrderStatus.INIT);
orderMapper.insert(order);
// 2. 扣减库存(远程调用)
InventoryResult invResult = inventoryClient.deduct(
request.getProductId(), request.getQuantity()
);
if (!invResult.isSuccess()) {
throw new BusinessException("库存不足");
}
// 3. 冻结资金(远程调用)
PaymentResult payResult = paymentClient.freeze(
request.getUserId(), order.getTotalAmount()
);
if (!payResult.isSuccess()) {
throw new BusinessException("余额不足");
}
order.setStatus(OrderStatus.CREATED);
orderMapper.updateById(order);
return OrderResult.success(order);
}
}
AT模式的工作原理:Seata在SQL执行前自动生成前镜像(before-image),执行后生成后镜像(after-image),写入undo_log表。回滚时根据前镜像反向补偿。全局事务提交时异步清理undo_log。
Saga模式:长流程业务编排方案
AT模式适合执行时间短(秒级)的分布式事务。对于执行时间长的业务流程(如供应商发货、物流签收),Saga模式更合适——它不锁资源,通过正向执行+反向补偿来保证最终一致性。
// Seata Saga状态机定义
// resources/saga/create-order.json
{
"Name": "createOrder",
"Comment": "订单创建Saga流程",
"StartState": "CreateOrder",
"States": {
"CreateOrder": {
"Type": "ServiceTask",
"ServiceName": "orderService",
"ServiceMethod": "create",
"CompensateMethod": "cancel",
"Next": "DeductInventory",
"Input": ["$.request"]
},
"DeductInventory": {
"Type": "ServiceTask",
"ServiceName": "inventoryService",
"ServiceMethod": "deduct",
"CompensateMethod": "restore",
"Next": "FreezePayment",
"Input": ["$.request.productId", "$.request.quantity"]
},
"FreezePayment": {
"Type": "ServiceTask",
"ServiceName": "paymentService",
"ServiceMethod": "freeze",
"CompensateMethod": "unfreeze",
"Next": "Succeed",
"Input": ["$.request.userId", "$.createOrder.totalAmount"]
},
"Succeed": {
"Type": "Succeed"
}
}
}
消息中间件实现最终一致性
RocketMQ事务消息是另一种分布式事务实现路径——不需要引入Seata这样的独立事务协调器,直接复用消息中间件做事务保障。
// RocketMQ事务消息生产者
@Component
public class OrderTransactionProducer {
@Autowired
private RocketMQTemplate rocketMQTemplate;
public void sendOrderTransaction(Order order) {
rocketMQTemplate.sendMessageInTransaction(
"order-topic",
MessageBuilder.withPayload(order)
.setHeader("orderId", order.getId())
.build(),
order // 传递给本地事务的参数
);
}
// 本地事务执行器
@RocketMQTransactionListener
static class OrderTransactionListener implements RocketMQLocalTransactionListener {
@Autowired
private OrderService orderService;
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
try {
Order order = (Order) arg;
orderService.createOrder(order);
return RocketMQLocalTransactionState.COMMIT;
} catch (Exception e) {
return RocketMQLocalTransactionState.ROLLBACK;
}
}
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
String orderId = (String) msg.getHeaders().get("orderId");
Order order = orderService.getById(orderId);
if (order != null && order.getStatus() == OrderStatus.CREATED) {
return RocketMQLocalTransactionState.COMMIT;
}
return RocketMQLocalTransactionState.ROLLBACK;
}
}
}
服务治理与API接口规范
分布式事务引入后,服务间调用关系的治理变得更重要。Sentinel做限流熔断、Nacos做服务注册发现、SkyWalking做链路追踪——这三件套是微服务治理的基础设施。关键配置:
// Sentinel限流规则(针对库存服务)
@SentinelResource(value = "deduct-inventory",
blockHandler = "handleBlock",
fallback = "handleFallback")
public InventoryResult deduct(String productId, int quantity) {
return inventoryService.deduct(productId, quantity);
}
public InventoryResult handleBlock(String productId, int quantity, BlockException ex) {
log.warn("库存扣减被限流: product={}", productId);
return InventoryResult.fail("系统繁忙,请稍后重试");
}
public InventoryResult handleFallback(String productId, int quantity, Throwable ex) {
log.error("库存扣降级: product={}, error={}", productId, ex.getMessage());
return InventoryResult.fail("服务暂时不可用");
}
业务中台建设中,分布式事务是技术架构的”承重墙”。选型时记住一个原则:能不用分布式事务就不用——通过业务流程重组(将跨服务操作合并为单服务内的本地事务)避免分布式事务,永远是成本最低的方案。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-wei-fu-wu-jia-gou-xia-de-fen-bu-shi-shi-wu-jie/