分布式事务处理中的事务幂等性实现
最近在参与一个分布式订单系统的设计,遇到了一个让人头疼的问题:如何在分布式环境下保证事务的幂等性。这可不是理论上的概念,而是实实在在的生产环境问题。
问题背景
我们的订单系统涉及多个服务:用户服务、库存服务、支付服务。用户下单后,需要调用这三个服务完成整个流程。如果在某个环节失败了,需要重试,但如果重试时没有幂等性控制,就会出现重复扣款、重复发货的问题。
我的踩坑经历
一开始我采用了简单的数据库唯一索引来保证幂等性,代码如下:
@Transactional
public void processOrder(String orderId, String userId) {
// 插入订单记录
orderMapper.insert(new Order(orderId, userId, "processing"));
// 调用库存服务
inventoryService.reduceStock(orderId);
// 调用支付服务
paymentService.processPayment(orderId);
}
结果发现,当库存服务调用失败后重试,订单状态被重复创建。后来我改用了分布式锁:
public boolean processOrder(String orderId, String userId) {
String lockKey = "order_lock:" + orderId;
try {
if (redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", 30, TimeUnit.SECONDS)) {
// 业务逻辑
return true;
}
return false;
} finally {
redisTemplate.delete(lockKey);
}
}
但是这个方案在高并发场景下性能很差,而且一旦锁超时没释放,就会出现并发问题。
最终方案
最后采用了幂等性ID + 状态机的方案:
public class OrderService {
private static final String ORDER_PREFIX = "ORDER_";
public void createOrder(String orderId, String userId) {
// 生成幂等性标识
String idempotentKey = ORDER_PREFIX + orderId;
// 检查是否已经处理过
if (redisTemplate.hasKey(idempotentKey)) {
return; // 已经处理过了,直接返回
}
try {
// 业务逻辑
orderMapper.insert(new Order(orderId, userId, "pending"));
inventoryService.reduceStock(orderId);
paymentService.processPayment(orderId);
// 标记已处理
redisTemplate.opsForValue().set(idempotentKey, "processed", 24, TimeUnit.HOURS);
} catch (Exception e) {
// 失败时清除缓存,允许下次重试
redisTemplate.delete(idempotentKey);
throw e;
}
}
}
实践建议
- 需要结合业务场景选择合适的幂等性实现方式
- 不要只依赖数据库唯一索引,要考虑分布式环境下的复杂情况
- 在高并发场景下,分布式锁的性能开销需要仔细评估
- 重试机制和幂等性控制要配合使用
这个踩坑经历让我深刻体会到,分布式系统的设计不能想当然,必须在真实环境中反复验证。

讨论