在微服务架构与分布式系统盛行的今天,数据一致性问题成为开发者必须直面的核心挑战。传统的本地事务(ACID)在跨服务、跨数据库的场景下显得力不从心,分布式事务应运而生。本文将从实战角度出发,深入剖析6种主流的分布式事务解决方案,并附上可运行的代码示例,助你根据业务场景精准选型。### 1. 两阶段提交(2PC/XA)原理:引入全局事务管理器(TM),分两阶段协调多个资源管理器(RM)。阶段一:TM询问所有RM是否可提交;阶段二:若全部同意则全局提交,否则全局回滚。优点:强一致性,实现简单。缺点:存在同步阻塞,协调者单点,极端情况下可能阻塞(如协调者宕机)。适用场景:对一致性要求极高、并发量低的内部系统。代码示例(Java + JTA):javaimport javax.transaction.UserTransaction;import javax.naming.InitialContext;public class TwoPhaseCommitDemo { public void transfer() throws Exception { InitialContext ctx = new InitialContext(); UserTransaction utx = (UserTransaction) ctx.lookup("java:comp/UserTransaction"); try { utx.begin(); // 操作数据库A jdbcA.execute("UPDATE account SET balance = balance - 100 WHERE id = 1"); // 操作数据库B jdbcB.execute("UPDATE account SET balance = balance + 100 WHERE id = 2"); utx.commit(); // 全局提交 } catch (Exception e) { utx.rollback(); // 全局回滚 throw e; } }}### 2. 三阶段提交(3PC)原理:在2PC基础上引入“准备提交”阶段(CanCommit, PreCommit, DoCommit),增加超时机制,降低阻塞风险。优点:解决协调者故障导致的阻塞问题。缺点:实现复杂,极端情况仍无法保证完全一致性。适用场景:对2PC阻塞敏感,但可容忍少量不一致的分布式系统。代码示例(伪代码):python# 三阶段提交状态机示意class ThreePhaseCommit: def can_commit(self, participants): for p in participants: if not p.prepare(): return False return True def pre_commit(self, participants): for p in participants: p.pre_commit() # 写入undo日志 def do_commit(self, participants): for p in participants: p.commit() # 最终提交### 3. 本地消息表(异步确保)原理:核心思路是“最终一致性”。在业务操作所在的数据库中创建消息表,将业务操作与写消息表放在同一个本地事务中,然后通过定时任务或消息队列将消息发送给下游服务,下游消费成功后回调确认。优点:无强依赖,性能较好,可靠性高。缺点:需要额外维护消息表,存在消息重复消费问题(需幂等)。适用场景:订单系统、支付系统等对一致性要求中等、性能要求高的场景。代码示例(Spring + MyBatis):java@Transactionalpublic void createOrder(Order order) { // 1. 插入订单表(本地事务) orderMapper.insert(order); // 2. 插入消息表(同一本地事务) MessageRecord msg = new MessageRecord(); msg.setMsgId(UUID.randomUUID().toString()); msg.setContent(JSON.toJSONString(order)); msg.setStatus("pending"); messageMapper.insert(msg); // 3. 事务提交后,定时任务扫描消息表发送MQ}@Scheduled(cron = "0/5 * * * * ?")public void sendMessage() { List<MessageRecord> pendingList = messageMapper.selectPending(); for (MessageRecord record : pendingList) { try { mqTemplate.send("order_topic", record.getContent()); record.setStatus("sent"); messageMapper.update(record); } catch (Exception e) { // 重试或告警 } }}### 4. 事务消息(RocketMQ)原理:利用消息队列的事务消息特性。发送方先发送“半消息”(对消费者不可见),然后执行本地事务,根据结果提交或回滚半消息。消息队列在超时后主动回查事务状态。优点:解耦性强,无需额外消息表,可靠性高。缺点:依赖特定MQ(如RocketMQ),对回查接口有要求。适用场景:电商下单、库存扣减等异步解耦场景。代码示例(RocketMQ):java// 发送事务消息TransactionMQProducer producer = new TransactionMQProducer("tx_producer");producer.setTransactionListener(new TransactionListener() { @Override public LocalTransactionState executeLocalTransaction(Message msg, Object arg) { try { // 执行本地业务(如扣减库存) orderService.createOrder(arg); return LocalTransactionState.COMMIT_MESSAGE; } catch (Exception e) { return LocalTransactionState.ROLLBACK_MESSAGE; } } @Override public LocalTransactionState checkLocalTransaction(MessageExt msg) { // 回查本地事务状态 String orderId = msg.getKeys(); return orderService.isOrderExists(orderId) ? LocalTransactionState.COMMIT_MESSAGE : LocalTransactionState.ROLLBACK_MESSAGE; }});// 发送半消息Message msg = new Message("order_topic", "order", orderId, orderInfo.getBytes());SendResult sendResult = producer.sendMessageInTransaction(msg, orderInfo);### 5. TCC(Try-Confirm-Cancel)原理:业务层面拆分为三个操作:Try(预留资源)、Confirm(确认执行)、Cancel(回滚释放)。每个参与方实现这三个接口,由协调者统一调度。优点:无阻塞,性能高,灵活性好。缺点:业务侵入性强,需要编写大量补偿逻辑,实现复杂。适用场景:资金类操作(转账、支付)、票务预订等需要强资源控制的场景。代码示例(Java 接口定义):javapublic interface TccAction { // 预留资源(如冻结余额) boolean try() throws Exception; // 确认执行(扣减冻结金额) boolean confirm() throws Exception; // 取消(解冻余额) boolean cancel() throws Exception;}// 协调器伪代码public class TccCoordinator { public void doTcc(List<TccAction> actions) { // Try阶段 for (TccAction action : actions) { if (!action.try()) { // 执行Cancel for (int i = actions.size()-1; i >= 0; i--) { actions.get(i).cancel(); } return; } } // Confirm阶段 for (TccAction action : actions) { action.confirm(); } }}### 6. Saga 模式原理:将长事务拆分为一系列本地事务,每个事务有对应的补偿操作。若某一步失败,则依次调用之前的补偿操作回滚。优点:无阻塞,适合长事务,性能较好。缺点:隔离性弱(事务间未提交的数据不可见),需要设计补偿操作。适用场景:旅游预订(订机票+酒店)、订单聚合等跨服务多步骤流程。代码示例(Spring Boot + Saga):java// 定义Saga步骤@Componentpublic class SagaOrderFlow { @Autowired private OrderService orderService; @Autowired private InventoryService inventoryService; @Autowired private PaymentService paymentService; // 主流程 public void createOrderSaga() { try { Long orderId = orderService.createOrder(); // 步骤1 inventoryService.deductStock(orderId); // 步骤2 paymentService.pay(orderId); // 步骤3 } catch (Exception e) { // 补偿:反向调用 paymentService.refund(orderId); inventoryService.addStock(orderId); orderService.cancelOrder(orderId); } }}### 总结分布式事务没有“银弹”,每种方案都有其特定的适用场景与代价:-强一致性优先:选择2PC/3PC,但需接受性能与可用性折损。-最终一致性优先:本地消息表、事务消息、Saga是常见选择,其中事务消息最优雅,Saga适合长事务。-资源控制需求:TCC虽复杂,但对资金类操作最为安全。实际开发中,建议先明确业务对一致性的容忍度、吞吐量要求以及团队技术栈,再做出选型。同时,务必考虑幂等设计、重试机制与监控告警,以确保系统的鲁棒性。分布式事务的实践是架构师必修课,希望本文能为你在技术决策中提供有价值的参考。
# 总结分布式事务没有“银弹”,每种方案都有其特定的适用场景与代价