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

张开发
2026/4/22 17:42:21 15 分钟阅读

分享文章

每日Java面试场景题知识点之-分布式事务
每日Java面试场景题知识点之-分布式事务一、分布式事务问题由来在单体应用时代我们可以依赖数据库本地事务ACID来保证数据的一致性。一个业务操作涉及的多个数据库更新操作可以放在同一个事务中要么全部成功要么全部回滚。然而随着业务发展系统拆分为多个微服务后情况变得复杂起来。一个业务操作可能需要调用多个微服务每个微服务操作自己的数据库。此时本地事务已经无法跨服务保证数据一致性这就是分布式事务问题的由来。典型场景举例电商下单场景用户下单后需要同时完成以下操作在订单服务创建订单记录调用库存服务扣减库存调用账户服务扣减余额调用积分服务增加用户积分这些操作涉及多个独立的数据源任何一个环节失败都会导致数据不一致。如果订单创建成功但库存扣减失败就会出现超卖问题如果库存扣减成功但余额扣减失败就会出现用户未付款但库存已扣减的问题。二、分布式事务的核心挑战2.1 CAP理论CAP理论指出一个分布式系统最多只能同时满足以下三个特性中的两个一致性Consistency所有节点同时看到相同的数据可用性Availability每次请求都能得到响应成功或失败分区容错性Partition Tolerance系统在网络分区的情况下仍能继续运行在分布式系统中网络分区是不可避免的P因此我们只能在CP一致性和分区容错和AP可用性和分区容错之间做权衡。2.2 BASE理论BASE理论是对CAP理论的补充提出基本可用、软状态和最终一致性的概念适合互联网场景下的分布式事务处理。基本可用Basically Available系统出现故障时允许损失部分可用性软状态Soft State允许数据存在中间状态不影响系统可用性最终一致性Eventually Consistent经过一段时间后所有节点的数据最终达到一致三、分布式事务解决方案详解3.1 两阶段提交2PC两阶段提交是最经典的强一致性分布式事务协议由协调者和参与者组成。第一阶段准备阶段协调者向所有参与者发送准备请求参与者执行事务操作但不提交参与者向协调者反馈是否可以提交第二阶段提交阶段如果所有参与者都同意提交协调者发送提交指令如果有任一参与者拒绝协调者发送回滚指令参与者根据指令执行提交或回滚2PC的缺点同步阻塞参与者在等待协调者指令期间处于阻塞状态单点故障协调者故障会导致所有参与者阻塞数据不一致协调者发送提交指令后部分参与者宕机3.2 三阶段提交3PC3PC在2PC的基础上增加了预准备阶段降低了阻塞时间。阶段划分CanCommit协调者询问参与者是否可以执行事务PreCommit参与者预执行事务锁定资源DoCommit协调者根据参与者反馈决定提交或回滚3PC的改进与局限改进点引入超时机制参与者可以自主决策 局限性仍然存在单点故障风险且增加了网络通信开销3.3 TCCTry-Confirm-CancelTCC是应用层的分布式事务解决方案每个业务操作需要实现三个方法。TCC工作流程Try阶段预留资源检查业务条件Confirm阶段确认执行使用Try阶段预留的资源Cancel阶段取消操作释放Try阶段预留的资源TCC示例订单支付场景public interface OrderService { // Try阶段创建预扣款订单 Compensable boolean prepareOrder(Order order); // Confirm阶段确认订单 boolean confirmOrder(Order order); // Cancel阶段取消订单 boolean cancelOrder(Order order); } public interface AccountService { // Try阶段预扣余额 Compensable boolean prepareDeduct(String userId, BigDecimal amount); // Confirm阶段确认扣款 boolean confirmDeduct(String userId, BigDecimal amount); // Cancel阶段取消扣款 boolean cancelDeduct(String userId, BigDecimal amount); }TCC优缺点优点性能较好不依赖数据库本地事务 缺点业务代码侵入性强需要实现三个方法开发成本高3.4 Saga模式Saga将长事务拆分为多个本地短事务每个短事务都有对应的补偿事务。Saga执行方式正向执行依次执行各个本地事务补偿执行如果某一步失败按相反顺序执行补偿事务Saga实现示例public class OrderSaga { public void createOrder(Order order) { try { // 步骤1创建订单 orderService.create(order); // 步骤2扣减库存 inventoryService.deduct(order.getProductId(), order.getQuantity()); // 步骤3扣减余额 accountService.deduct(order.getUserId(), order.getAmount()); } catch (Exception e) { // 补偿操作 accountService.compensateDeduct(order.getUserId(), order.getAmount()); inventoryService.compensateDeduct(order.getProductId(), order.getQuantity()); orderService.compensateCreate(order.getId()); throw e; } } }Saga优缺点优点适合长业务流程可以跨多个服务 缺点无法保证隔离性存在脏读问题3.5 本地消息表本地消息表方案利用本地事务保证业务操作和消息发送的原子性。实现原理业务操作和消息记录在同一个本地事务中写入定时任务扫描消息表发送未确认的消息消费者消费成功后更新消息状态本地消息表示例Service public class OrderServiceImpl implements OrderService { Autowired private OrderMapper orderMapper; Autowired private MessageMapper messageMapper; Autowired private MessageProducer messageProducer; Transactional public void createOrder(Order order) { // 1. 创建订单 orderMapper.insert(order); // 2. 创建消息记录与订单在同一个事务中 Message message new Message(); message.setTopic(order.created); message.setContent(JSON.toJSONString(order)); message.setStatus(MessageStatus.SENDING); messageMapper.insert(message); } } Component public class MessageTask { Scheduled(fixedDelay 5000) public void sendPendingMessages() { ListMessage messages messageMapper.selectByStatus(MessageStatus.SENDING); for (Message message : messages) { try { messageProducer.send(message.getTopic(), message.getContent()); messageMapper.updateStatus(message.getId(), MessageStatus.SENT); } catch (Exception e) { log.error(发送消息失败, e); } } } }3.6 事务消息事务消息利用消息中间件如RocketMQ的事务消息机制保证最终一致性。事务消息流程发送半消息消息不可见不参与消费执行本地事务提交或回滚消息消息中间件回查事务状态RocketMQ事务消息示例Service public class OrderTransactionListener implements TransactionListener { Autowired private OrderService orderService; Override public LocalTransactionState executeLocalTransaction(Message msg, Object arg) { try { Order order JSON.parseObject(new String(msg.getBody()), Order.class); orderService.createOrder(order); return LocalTransactionState.COMMIT_MESSAGE; } catch (Exception e) { return LocalTransactionState.ROLLBACK_MESSAGE; } } Override public LocalTransactionState checkLocalTransaction(MessageExt msg) { Order order orderService.queryByTxId(msg.getTransactionId()); if (order ! null) { return LocalTransactionState.COMMIT_MESSAGE; } return LocalTransactionState.UNKNOW; } } Component public class OrderMessageProducer { Autowired private RocketMQTemplate rocketMQTemplate; public void sendOrderMessage(Order order) { MessageOrder message MessageBuilder.withPayload(order).build(); rocketMQTemplate.sendMessageInTransaction( order-group, order-topic, message, null ); } }3.7 Seata分布式事务框架Seata是阿里巴巴开源的分布式事务解决方案支持多种事务模式。Seata架构TCTransaction Coordinator事务协调器TMTransaction Manager事务管理器RMResource Manager资源管理器Seata AT模式示例Service public class OrderServiceImpl { Autowired private OrderMapper orderMapper; Autowired private InventoryService inventoryService; Autowired private AccountService accountService; GlobalTransactional(name create-order, rollbackFor Exception.class) public void createOrder(Order order) { // 创建订单 orderMapper.insert(order); // 调用库存服务远程调用自动纳入全局事务 inventoryService.deduct(order.getProductId(), order.getQuantity()); // 调用账户服务远程调用自动纳入全局事务 accountService.deduct(order.getUserId(), order.getAmount()); } }Seata TCC模式示例LocalTCC public interface InventoryService { TwoPhaseBusinessAction(name prepareDeduct, commitMethod confirmDeduct, rollbackMethod cancelDeduct) boolean prepareDeduct(BusinessActionContextParameter(paramName productId) String productId, BusinessActionContextParameter(paramName quantity) int quantity); boolean confirmDeduct(BusinessActionContext context); boolean cancelDeduct(BusinessActionContext context); } Service public class InventoryServiceImpl implements InventoryService { Autowired private InventoryMapper inventoryMapper; Autowired private InventoryReservedMapper reservedMapper; Override public boolean prepareDeduct(String productId, int quantity) { // 检查库存 Inventory inventory inventoryMapper.selectByProductId(productId); if (inventory.getStock() quantity) { throw new RuntimeException(库存不足); } // 预留库存 InventoryReserved reserved new InventoryReserved(); reserved.setProductId(productId); reserved.setQuantity(quantity); reservedMapper.insert(reserved); return true; } Override public boolean confirmDeduct(BusinessActionContext context) { String productId context.getActionContext(productId).toString(); int quantity Integer.parseInt(context.getActionContext(quantity).toString()); // 实际扣减库存 inventoryMapper.deductStock(productId, quantity); // 删除预留记录 reservedMapper.deleteByBizKey(context.getActionContext(bizKey).toString()); return true; } Override public boolean cancelDeduct(BusinessActionContext context) { // 删除预留记录 reservedMapper.deleteByBizKey(context.getActionContext(bizKey).toString()); return true; } }四、分布式事务方案对比4.1 强一致性方案2PC、3PC、XA保证强一致性但性能较差阻塞严重适合对一致性要求极高且并发量不大的场景。4.2 最终一致性方案TCC、Saga、本地消息表、事务消息保证最终一致性性能较好适合互联网高并发场景。4.3 方案选择建议高并发、允许最终不一致TCC、Saga、事务消息一致性要求高、并发量不大2PC、XA、Seata AT跨服务长流程Saga模式简单场景本地消息表五、分布式事务最佳实践5.1 设计原则尽量避免分布式事务通过业务设计减少跨服务事务选择合适的一致性级别根据业务需求选择强一致或最终一致幂等性设计所有操作都要支持幂等补偿机制设计合理的补偿操作5.2 注意事项超时设置合理设置超时时间避免长时间阻塞重试机制设计合理的重试策略监控告警对分布式事务执行情况进行监控日志记录完整记录事务执行过程便于排查问题六、面试高频问题什么是分布式事务为什么需要分布式事务CAP理论和BASE理论分别是什么2PC和3PC的区别是什么TCC的实现原理是什么有哪些优缺点Saga模式如何保证最终一致性本地消息表和事务消息有什么区别Seata支持哪些事务模式AT模式和TCC模式的区别是什么如何选择合适的分布式事务方案感谢读者观看

更多文章