每日Java面试场景题知识点之-分布式事务处理

每日Java面试场景题知识点之-分布式事务处理

场景描述

在一个电商系统中,用户下单操作涉及到多个微服务:订单服务、库存服务、积分服务、支付服务。当用户下单时,需要保证这些操作要么全部成功,要么全部失败,这就是典型的分布式事务场景。

实际业务场景: 用户点击下单按钮后,系统需要执行以下操作:

  1. 订单服务创建订单
  2. 库存服务扣减库存
  3. 积分服务增加用户积分
  4. 支付服务处理支付

问题分析

核心问题

在单体应用中,我们可以使用数据库的事务来保证数据的一致性。但在微服务架构中,每个服务都有自己独立的数据库,传统的事务无法跨服务生效。

技术挑战

  1. 数据一致性:如何保证跨服务的数据一致性
  2. 性能影响:分布式事务的实现往往伴随着性能开销
  3. 故障恢复:部分服务失败时的补偿机制
  4. 监控难度:分布式环境下的事务状态监控

技术栈与解决方案

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框架提供了开箱即用的分布式事务能力,消息队列方案则更适合异步业务场景。无论选择哪种方案,都需要充分考虑数据一致性、性能、可用性的平衡,并做好监控和容灾设计。

感谢读者的观看,希望本文能够帮助您更好地理解和掌握分布式事务处理技术!

相关推荐
L***d6703 小时前
Spring Boot 各种事务操作实战(自动回滚、手动回滚、部分回滚)
java·数据库·spring boot
凌波粒3 小时前
Springboot基础教程(3)--自动装配原理/静态资源处理/欢迎页
java·spring boot·后端
likuolei3 小时前
XSL-FO 软件
java·开发语言·前端·数据库
凌波粒3 小时前
SpringBoot基础教程(2)--yaml/配置文件注入/数据校验/多环境配置
java·spring boot·后端·spring
S***26753 小时前
Spring Boot环境配置
java·spring boot·后端
6***83053 小时前
什么是Spring Boot 应用开发?
java·spring boot·后端
毕设源码柳学姐3 小时前
计算机毕设 java 智慧社区服务系统 SSM 框架社区生活平台 Java 开发的便民服务与互动系统
java·开发语言·生活
U***l8323 小时前
【postgresql】分区表管理
java·数据库·postgresql
倚肆3 小时前
MyBatis-Plus Mapper 接口方法详解
java·mybatis