聊聊幂等的几种实现方案

在工作中经常会提到一个词“幂等”,什么是幂等呢?也就是对于同一个操作,调用多次结果都是一样的

场景:
订单系统和仓储系统是单独的两个微服务,当订单系统创建发货单调用接口推送给下游仓储系统,仓储系统创建对应的出库单。

假设订单系统接收到接口调用超时,进行重试,再次调用接口进行推送,对于订单系统创建的一个发货单,推送给仓储系统应该是一个幂等的操作,不管调用几次推送操作,仓储系统也应该只生成这个发货单对应的唯一出库单,否则发货单商品A计划发10个,结果生成重复的出库单都出10个就有问题了。

为了解决这个问题,能想到的两种方案:

方案一:
下游系统(如上面场景中的仓储系统)提供一个查询接口,上游系统(如上面场景中的订单系统)在进行重试时,先调用这个查询接口,看发货单是否已存在,如果已存在那么就不再重复推送,否则进行重试推送。

方案二:
上游系统不单独查询,直接进行重试推送,下游系统自己处理,判断发货单如果已存在直接返回成功,否则进行新增出库单的操作。也就是下游接口支持幂等

上面的方案一还需要提供额外的接口,而且对于一些其他的场景,比如MQ重复消息消费的场景,为了保证幂等,让上游系统处理并不合适,更好的方案是下游系统对外提供的接口支持幂等。那么下游系统在设计接口时如何实现幂等呢,大概总结有以下几种实现方案:

1、select +insert+唯一索引

(1) 先根据唯一标识查询数据
(2) 判断数据是否已存在,如果已存在直接返回成功
(3) 如果上一步判断为否再进行insert操作并返回结果

注意:这里还需要借助唯一索引,因为在高并发场景下,可能两个请求同时select的时候数据都不存在,都执行insert就有问题了

2、直接insert+唯一索引

这种实现方式同第1种流程基本一样,不同点在于没有查询这一步,利用唯一索引冲突来过滤重复请求,适用于重复请求的概率较低的场景。

3、分布式锁

(1) 线程A执行先获取分布式锁,
(2) 线程A获取锁成功后查询数据是否存在,存在则直接返回成功,否则执行业务逻辑操作
(3) 线程A执行完毕释放分布式锁
(4) 线程B执行也先获取分布式锁,等待线程A释放后获取成功后再执行查询数据、判断数据、执行业务操作

4、悲观锁

这种实现方式使用数据库的for update

(1) 线程A使用select…for update查询数据,对当前数据加锁
(2) 其他线程执行select …for update时等待线程A释放
这种方式select…from table for update where column=xxx的条件column不是索引列或主键会锁表,如果业务逻辑执行耗时较长会很影响性能,不推荐使用

5、乐观锁

这种实现方式比较常见的在数据库中增加版本号列,更新数据时判断版本号是否一致。或者有些业务单据表都有状态列,更新数据时可以根据状态来加锁,如:update table set status=3 where status=2

以上这几个实现幂等的实现方案,每一个方案并不是针对所有场景,根据不同的场景来选择不同的方案。实现幂等效果有点类似过滤重复请求,所有有些时候处理重复请求也可以考虑采用这些实现方式。

你可能感兴趣的:(Java,java,幂等)