幂等性

1、含义

对资源一次或多次请求所产生的结果是相同的(网络超时情况除外),也就是说一次或多次请求对资源的影响是相同的。

可以说,幂等性要求在请求成功的情况下,多次请求对系统的影响是一致的,并不会因为哪一次请求,导致不同的结果或影响。

2、常见的幂等性操作

在DML中,某些语句可以体现出幂等性:

   select * from user where id = 1    // 保持幂等性

   update user set name = 'tom' where id = 1    // 保持幂等性

   delete * from user where id = 1    // 保持幂等性

   delete * from user where name like '%tom%'    // 不保持幂等性

   update user set  age = age + 1 where id = 1    // 不保持幂等性

关联到服务端方法的幂等性上,即GET、PUT、POST、DELETE等请求方法上,可见GET是保持幂等性的,而PUT、POST、DELETE是根据业务场景来决定是否是幂等性的,即不一定是幂等性的。

3、常见场景

在支付系统中,因为网络原因请求无法到达,或者前端操作抖动等原因,导致支付请求多次重复提交;又或者退款申请的多次调用(本人曾经编写过微信退款回调的功能,并创建了一个定时任务去查询微信退款结果。微信回调我的接口和定时任务查询微信退款结果两者同时发生了,造成了幂等问题)等;服务的相应接口都需要保持幂等性,以防止产生副作用,即出现系统数据状态改变,导致数据错误。

4、实现方法

1)乐观锁

一般给某条数据加上version字段,在每次更新该数据时,version自增1:

update user set name = 'Joe' , version = version + 1 where id = 1 and version = #{version}

2) 去重索引

对于订单,可以在表中建立关于订单号orderNo的唯一索引,当多次相同订单号的订单请求到达时,数据库会因为唯一索引兜底,保证只有一个请求执行成功。

3)分布式锁

 

一般借助redis锁来实现幂等性,对于支付订单,当支付请求到达时,根据orderNo设置redis锁,等待请求完成后再释放锁。在加锁后,判断订单的是否已存在,且其支付状态如何,做出相应的处理。使用redis锁(缓存)的好处是效率比唯一索引去重高。

4)token令牌

分为两步:申请token并存入redis中,下单请求。

在下单请求时,将申请的token传入header,判断header中的token作为key是否在redis中存在;如果存在,则删除该key(判断存在和删除该key必须为原子操作),并执行业务逻辑;如果不存在,则请求是非法的。关键点在于只能申请一次token,保障了幂等性。

5)状态机

每一次操作会导致资源处于不同的状态上,当在某一种状态上,只能执行特定的操作。

5. 参考博客文档:

https://www.cnblogs.com/javalyy/p/8882144.html(这篇博文写得不错)

https://www.cnblogs.com/dongl961230/p/14817223.html

posted @ 2023-06-25 17:07  朝云出岫  阅读(92)  评论(0)    收藏  举报