共计 2738 个字符,预计需要花费 7 分钟才能阅读完成。
业务场景与挑战
在电商系统中,用户下单往往涉及多个微服务的协同操作。例如:

- 订单服务 创建订单记录
- 库存服务 扣减商品库存
- 支付服务 处理支付流程
- 积分服务 增加用户积分
这种场景下,如果某个服务调用失败(如库存不足),系统必须保证所有服务的数据状态能够回滚,否则会出现 ” 已支付但无库存 ” 的业务异常。这就是典型的分布式事务强一致性需求。
主流方案对比
1. 2PC(两阶段提交)
- 优点:强一致性保证
- 缺点:
- 同步阻塞(性能差,通常 QPS<500)
- 协调者单点故障风险
- 不适合长事务
2. Saga 模式
- 实现方式:
- 将事务拆分为多个本地事务
- 通过事件 / 命令驱动执行
- 失败时触发补偿操作
- 特点:
- 最终一致性(QPS 可达 3000+)
- 业务侵入性中等
- 需设计完善的补偿机制
3. TCC 模式
- 核心阶段:
- Try:预留资源
- Confirm:确认操作
- Cancel:取消预留
- 优势:
- 高并发支持(QPS 可达 5000+)
- 避免长事务锁竞争
- 挑战:
- 业务代码复杂度高
- 需处理空回滚和悬挂问题
4. 本地消息表
- 适用场景:
- 对实时性要求不高的业务
- 跨系统最终一致性
- 局限:
- 消息积压风险
- 需额外维护消息状态
Seata 实战实现
环境配置
# application.yml 关键配置
seata:
enabled: true
application-id: order-service
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: default
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
cluster: default
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
group: SEATA_GROUP
TCC 模式代码示例
// 库存服务 TCC 接口
public interface InventoryTccService {@TwoPhaseBusinessAction(name = "prepareDeduct", commitMethod = "confirm", rollbackMethod = "cancel")
boolean prepareDeduct(@BusinessActionContextParameter(paramName = "productId") String productId,
@BusinessActionContextParameter(paramName = "count") int count);
boolean confirm(BusinessActionContext context);
boolean cancel(BusinessActionContext context);
}
// 实现类(关键防悬挂处理)@Service
public class InventoryTccServiceImpl implements InventoryTccService {
@Autowired
private InventoryMapper inventoryMapper;
@Override
public boolean prepareDeduct(String productId, int count) {
// 检查悬挂:如果已有 confirm/cancel 记录则直接返回
if(checkTransactionExists(productId)) {return false;}
// 预扣减(实际是冻结库存)return inventoryMapper.freezeStock(productId, count) > 0;
}
@Override
@Transactional
public boolean confirm(BusinessActionContext context) {String productId = context.getActionContext("productId");
int count = (int) context.getActionContext("count");
// 幂等检查
if(checkConfirmed(productId)) {return true;}
// 实际扣减库存
inventoryMapper.reduceStock(productId, count);
// 释放冻结
inventoryMapper.unfreezeStock(productId, count);
return true;
}
@Override
@Transactional
public boolean cancel(BusinessActionContext context) {String productId = context.getActionContext("productId");
int count = (int) context.getActionContext("count");
// 空回滚检查(没有 prepare 记录)if(!checkPrepareExists(productId)) {
// 插入空回滚记录
insertCancelRecord(productId);
return true;
}
// 恢复冻结库存
inventoryMapper.unfreezeStock(productId, count);
return true;
}
}
分布式 ID 生成
- 雪花算法(Snowflake)
- 64 位 ID = 时间戳(41bit) + 机器 ID(10bit) + 序列号(12bit)
- 优点:
- 趋势递增
- 高性能(单机每秒可生成 400W+)
-
缺点:
- 依赖系统时钟
- 机器 ID 需要配置
-
UUID
- 全局唯一但无序
- 存储空间大(32 字符)
- 索引效率低
建议:交易类业务优先使用雪花算法
性能压测数据
| 方案 | 线程数 | TPS | 平均 RT(ms) | 错误率 |
|---|---|---|---|---|
| 2PC | 100 | 320 | 310 | 0.2% |
| TCC | 100 | 4800 | 21 | 0.01% |
| Saga | 100 | 3500 | 29 | 0.05% |
测试环境:4C8G × 3 节点,MySQL 5.7,5000 并发用户
生产环境避坑指南
1. 事务日志表优化
- 必须索引:
- 事务 ID(主键)
- 业务键 + 状态联合索引
- 建议:
- 按月分表
- 归档历史数据
2. 网络分区处理
- 补偿策略:
- 设置合理超时时间(建议 30s)
- 实现异步补偿任务
- 人工干预接口
3. Seata 高可用
- Server 部署:
- 至少 3 节点集群
- 使用 Nacos/Zookeeper 注册中心
- 存储模式:
- 生产环境必须用 DB 模式(非 file)
- 监控:
- 对接 Prometheus
- 报警事务失败率 >0.1%
延伸思考
在跨语言微服务体系(如 Java+Go+Node.js)中,如何实现分布式事务?可能的方案包括:
- 基于 HTTP 协议的 Saga 实现
- 通过 Sidecar 代理(如 Service Mesh)
- 标准化事务事件协议
欢迎在评论区分享你的架构设计思路。
正文完
