5月27日 23:25
RPC 调用中分布式事务怎么保证一致性?
核心答案
RPC 调用跨服务操作数据,本地事务无法覆盖,必须用分布式事务方案保证一致性。核心思路只有两条路:强一致性(2PC/XA)或最终一致性(TCC/Saga/消息事务)。实际生产中,绝大多数场景选最终一致性。
为什么不用 2PC 解决一切?
2PC 通过协调者让所有参与者先准备再统一提交,理论上能保证强一致,但有两个致命问题:
- 同步阻塞:准备阶段所有参与者锁住资源,高并发下性能崩溃
- 单点故障:协调者挂了,参与者永远锁着等,整个系统卡死
3PC 加了超时机制和预提交阶段,减少了阻塞窗口,但网络分区时仍可能出现数据不一致,治标不治本。
生产中怎么选?
高并发短事务 → TCC
Try 预留资源、Confirm 确认执行、Cancel 回滚释放。性能好但代码侵入强,每个服务要写三个接口:
javapublic interface OrderTccService { boolean tryCreateOrder(Order order); // 预扣库存 void confirmCreateOrder(Long orderId); // 确认下单 void cancelCreateOrder(Long orderId); // 释放库存 }
关键点:Confirm 和 Cancel 必须幂等,网络重试不能导致重复扣减。
长流程多步骤 → Saga
把长事务拆成多个本地事务串行执行,每步配一个补偿操作。某步失败则反向执行已完成步骤的补偿。适合业务流程长的场景(如订单→支付→物流),但要接受中间态的脏读。
异步解耦 → 事务消息
RocketMQ 半消息机制:先发半消息 → 执行本地事务 → 提交或回滚消息。本地事务和消息发送原子性保证,消费端幂等消费即可。适合"下单后异步扣积分"这类场景。
快速落地 → Seata AT 模式
一行注解搞定,对业务代码几乎无侵入:
java@GlobalTransactional(rollbackFor = Exception.class) public void createOrder(Order order) { inventoryService.deduct(order.getProductId(), order.getQuantity()); orderMapper.insert(order); accountService.deduct(order.getUserId(), order.getAmount()); }
底层自动记录数据前后镜像,回滚时用镜像恢复。代价是性能比 TCC 低,适合一致性要求高但并发不极端的场景。
面试追问问什么?
- 幂等怎么设计? 数据库唯一键 + 状态机,消费端去重表
- Seata AT 性能瓶颈在哪? 全局锁竞争,热点数据场景退化为串行
- TCC 空回滚和悬挂怎么处理? Try 未执行就收到 Cancel 是空回滚,Try 在 Cancel 之后才执行是悬挂,都要靠事务控制表判状态