1. 背景
关于支付相关,订单相关以及一些涉及费用的操作在业务上都是要求接口具有幂等性的。否则在高并发的场景下,同一笔交易请求多次,则会造成损失,这是不可忽视的错误。
例如一笔订单,因为网络或者操作的原因,造成同时发起了两次申请。
一般的接口设计中,对于重复发起的交易都是先查询是否存在这笔订单,如果不存在,则继续进行,并插入一条记录,如果存在这笔交易,则返回订单处理中。这种设计在不存在并发的情况下是没有问题的。例子如下:
public class OrderService {
// 下单接口
public String execute(OrderData data){
// 先查询是否存在该订单
OrderData order = findOrder(data.getId());
if (null != order){
// 存在该订单说明重复下单,直接返回
return "订单正在处理中";
}
// do something
return "交易完成";
}
public OrderData findOrderAndInsert(String id){
// 通过id在数据库中查询
OrderData data = selectById(id);
// 存在数据返回
if ( null != data){
return data;
}
// 不存在则插入数据
OrderData order = new OrderData();
order.setId(id);
insertData(order);
return null;
}
}
但是在并发的场景下,两笔交易同时调用接口findOrderAndInsert()方法,
A请求查询订单,发现不存在,则插入一条记录,在插入这个操作还没完成的时候,B请求进来,因为A的记录还没有插入数据库,这个时候B查询订单,发现不存在,则也插入一条记录,并继续执行。如果两条记录没有唯一索引的限制,则会完成两笔交易。如果是支付交易,相当于我支付了两次。所以这样是不合理的。
2. 什么是幂等性
幂等性:在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“setTrue()”函数就是一个幂等函数,无论多次执行,其结果都是一样的.更复杂的操作幂等保证是利用唯一交易号(流水号)实现。
3. 如何实现幂等性
方法一 : synchronized关键字或者其他方式加锁
给findOrderAndInsert()方法加锁,用关键字synchronized加锁,来防止并发产生。但是这样做,对于每一次请求都要加锁,势必会影响接口的性能。
public