← 返回生产场景
❓问题1:下单要扣库存+加积分+发券,跨3个服务怎么保证一致核心痛点
订单服务管订单库,库存服务管库存库,@Transactional 只能管自己库的事务。
// 微服务时代:@Transactional 失效!
public void order() {
orderDao.insert(); // 订单库,已提交 ✅
stockService.decr(); // 库存库,网络超时 ❓
pointService.add(); // 积分库,没执行 ❌
} // 订单成了,库存没扣,钱亏了
本质:本地事务依赖数据库 undo log,跨了网络跨了库就没有"全局协调者"。分布式事务就是要补上这个协调者。
⚖️问题2:2PC(两阶段提交)— 强一致但太重强一致
协调者第一阶段让所有参与者 prepare,第二阶段统一提交或回滚。XA 就是 2PC。
// 阶段一:Prepare
协调者 → 全体: "准备好了吗?"
参与者: "OK(锁资源,写redo)"
// 阶段二:Commit
协调者 → 全体: "COMMIT!" // 有一个NO → 全体 ROLLBACK
❌ 缺点
① 同步阻塞 ② 协调者单点 ③ 性能差,不适合高并发
结论:2PC 只适合同机房、低并发、强一致(如银行核心)。互联网业务基本不用。
🔀问题3:TCC(Try-Confirm-Cancel)— 业务侵入大但性能好最终一致
public interface StockTcc {
boolean tryFreeze(String xid, int qty); // 冻结库存
boolean confirmDeduct(String xid); // 真扣
boolean cancelUnfreeze(String xid); // 解冻
}
// 表设计:加冻结字段 stock=100, frozen=0
// try(10) → stock=100,frozen=10 → confirm → stock=90,frozen=0
坑:空回滚(Try没执行Cancel来了)+ 幂等(Confirm/Cancel重试)+ 悬挂(Cancel先于Try)。需要事务日志表记录状态。
📜问题4:Saga — 长事务,用补偿代替回滚最终一致
// 旅行预订:订票→订房→租车,每步都有补偿
T1 订机票 → 补偿 C1 取消机票
T2 订酒店 → 补偿 C2 取消酒店
T3 租车 → 补偿 C3 取消租车
// T3失败 → 反向补偿: C2 → C1
对比TCC:Saga 没有 Try,直接执行真实操作,失败再补偿。中间状态可见(短暂不一致)。适合流程长、可容忍不一致的场景。
📋问题5:本地消息表 — 最终一致,生产最常用推荐
// 业务表和消息表同库,本地事务保证原子
@Transactional
public void createOrder() {
orderDao.insert(order); // 业务
msgDao.insert(new Msg("ADD_POINT", orderId)); // 消息
}
// 定时任务扫描未发送消息,发MQ,发成功改SENT
// 消费端必须幂等!
为什么可靠:① 业务和消息同库同事务,不会丢 ② 定时重试 ③ 消费端幂等。互联网公司90%用这个方案。
🚀问题6:Seata AT 模式 — 加个注解就行框架
@GlobalTransactional // 就这一个注解!
public void order(OrderDTO dto) {
orderService.create(dto);
stockService.decr(dto); // Seata 拦截 SQL,记录 before/after image
pointService.add(dto);
} // 任一失败,自动用 image 反向回滚
AT模式注意:① 有全局锁,写冲突时性能下降 ② 必须有主键。高并发写慎用,建议用TCC或本地消息表。
选型口诀:
① 强一致+低并发 → 2PC/XA(银行核心)
② 强一致+高并发+肯改业务 → TCC(资金/库存)
③ 最终一致+长流程 → Saga(订单履约)
④ 最终一致+解耦 → 本地消息表(最常用,90%场景)
⑤ 想偷懒+不强求性能 → Seata AT