每日Java面试场景题知识点之-分布式事务处理
场景描述
在一个电商系统中,用户下单操作涉及到多个微服务:订单服务、库存服务、积分服务、支付服务。当用户下单时,需要保证这些操作要么全部成功,要么全部失败,这就是典型的分布式事务场景。
实际业务场景: 用户点击下单按钮后,系统需要执行以下操作:
- 订单服务创建订单
- 库存服务扣减库存
- 积分服务增加用户积分
- 支付服务处理支付
问题分析
核心问题
在单体应用中,我们可以使用数据库的事务来保证数据的一致性。但在微服务架构中,每个服务都有自己独立的数据库,传统的事务无法跨服务生效。
技术挑战
- 数据一致性:如何保证跨服务的数据一致性
- 性能影响:分布式事务的实现往往伴随着性能开销
- 故障恢复:部分服务失败时的补偿机制
- 监控难度:分布式环境下的事务状态监控
技术栈与解决方案
1. CAP理论与BASE理论
CAP理论:
- 一致性(Consistency)
- 可用性(Availability)
- 分区容错性(Partition Tolerance)
在分布式系统中,我们通常选择AP,牺牲强一致性换取可用性。
BASE理论:
- 基本可用(Basically Available)
- 软状态(Soft State)
- 最终一致性(Eventually Consistent)
2. Seata框架解决方案
2.1 Seata AT模式
配置示例:
java
// OrderService
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StorageFeignClient storageFeignClient;
@GlobalTransactional(rollbackFor = Exception.class)
public void createOrder(Order order) {
// 1. 创建订单
orderMapper.insert(order);
// 2. 扣减库存
storageFeignClient.deduct(order.getProductId(), order.getCount());
// 3. 其他业务操作...
}
}
配置文件:
yaml
seata:
enabled: true
application-id: order-service
tx-service-group: my_test_tx_group
config:
type: nacos
nacos:
server-addr: localhost:8848
group: SEATA_GROUP
namespace:
registry:
type: nacos
nacos:
server-addr: localhost:8848
group: SEATA_GROUP
namespace:
2.2 TCC模式实现
java
// TCC接口定义
public interface OrderTccService {
@TwoPhaseBusinessAction(name = "createOrder", commitMethod = "commit", rollbackMethod = "rollback")
boolean createOrder(Order order);
boolean commit(BusinessActionContext context);
boolean rollback(BusinessActionContext context);
}
// 实现类
@Service
public class OrderTccServiceImpl implements OrderTccService {
@Override
public boolean createOrder(Order order) {
// Try阶段:资源检查和预留
Order reservedOrder = new Order();
reservedOrder.setId(order.getId());
reservedOrder.setStatus("RESERVED");
reservedOrder.setUserId(order.getUserId());
reservedOrder.setProductId(order.getProductId());
reservedOrder.setCount(order.getCount());
reservedOrder.setAmount(order.getAmount());
return orderMapper.insert(reservedOrder) > 0;
}
@Override
public boolean commit(BusinessActionContext context) {
// Confirm阶段:确认提交
String orderId = context.getActionContext("orderId").toString();
Order order = orderMapper.selectById(orderId);
order.setStatus("CONFIRMED");
return orderMapper.updateById(order) > 0;
}
@Override
public boolean rollback(BusinessActionContext context) {
// Cancel阶段:取消操作
String orderId = context.getActionContext("orderId").toString();
return orderMapper.deleteById(orderId) > 0;
}
}
3. 消息队列最终一致性方案
3.1 可靠消息投递
java
// 消息发送者
@Service
public class OrderMessageProducer {
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Transactional
public void sendOrderMessage(Order order) {
// 1. 本地事务操作
orderMapper.insert(order);
// 2. 发送消息
Message<OrderMessage> message = MessageBuilder
.withPayload(new OrderMessage(order.getId(), order.getUserId()))
.setHeader("orderId", order.getId())
.build();
// 发送事务消息
rocketMQTemplate.sendMessageInTransaction(
"order-topic:tag",
message,
order
);
}
}
// 消息监听者
@RocketMQMessageListener(
topic = "order-topic",
consumerGroup = "storage-consumer"
)
@Component
public class StorageMessageListener implements RocketMQListener<OrderMessage> {
@Autowired
private StorageService storageService;
@Override
public void onMessage(OrderMessage message) {
try {
// 消费消息,扣减库存
storageService.deductStock(message.getOrderId(), message.getUserId());
} catch (Exception e) {
// 消息消费失败,触发重试机制
throw new RuntimeException("库存扣减失败,消息将重试", e);
}
}
}
3.2 消息幂等性处理
java
@Service
public class StorageService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private StorageMapper storageMapper;
public boolean deductStock(String orderId, String userId) {
// 幂等性检查
String key = "stock_deduct_" + orderId;
Boolean exists = redisTemplate.hasKey(key);
if (Boolean.TRUE.equals(exists)) {
return true; // 已处理过
}
try {
// 扣减库存操作
int result = storageMapper.deductStock(orderId);
if (result > 0) {
// 标记已处理
redisTemplate.opsForValue().set(key, "1", 24, TimeUnit.HOURS);
return true;
}
return false;
} catch (Exception e) {
throw new RuntimeException("库存扣减失败", e);
}
}
}
最佳实践建议
1. 技术选型
- 强一致性场景:选择Seata AT模式
- 高性能要求:选择TCC模式
- 异步处理场景:选择消息队列方案
2. 性能优化
- 合理设置事务超时时间
- 使用本地缓存减少数据库访问
- 异步处理非核心业务逻辑
3. 监控与告警
- 分布式事务链路追踪
- 事务状态实时监控
- 异常情况及时告警
4. 容灾设计
- 服务降级策略
- 数据备份与恢复
- 灾难恢复预案
总结
分布式事务处理是微服务架构中的重要技术点,在实际项目中需要根据业务场景选择合适的解决方案。Seata框架提供了开箱即用的分布式事务能力,消息队列方案则更适合异步业务场景。无论选择哪种方案,都需要充分考虑数据一致性、性能、可用性的平衡,并做好监控和容灾设计。
感谢读者的观看,希望本文能够帮助您更好地理解和掌握分布式事务处理技术!