实现接口幂等性,防止并发操作,如何设计接口幂等方案?
当前是否有高并发的场景,如果是才需要处理接口幂等操作,如果不是就不需要处理。
插入更改操作,都要考虑高并发条件下的幂等性。
接口的幂等性实际上就是接口可重复调用,在调用方多次调用的情况下,接口最终得到的结果是一致的。
在高并发条件下,如果每秒200个线程注册,有可能num=-2, 验证成功,导致数据落库,所以应该是<= -1
1、redis:setNX实现分布式锁,防止多个相同操作同时执行。
2、mysql:select......for update (悲观锁) ,配合Spring事务一起使用,可以防止更新丢失。
3、前后端交互:前端调用接口时,先从后端获取一个令牌;请求接口时,后端直接删除当前令牌,如果删除成功,继续执行;如果删除失败,返回正在处理中。
4、mysql:使用数据库的唯一索引机制,比如对订单号创建唯一索引,防止订单号的重复。
5、mysql:通过数据库版本号实现幂等操作,通过mysql实现乐观锁(更新操作)。
6、redis分布式锁:在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁,这样其实是把多线程并发的锁的思路(redis的实现有多种方式,原子操作,递增,递减)
7、mysql联合唯一索引:一个特定的业务场景,三个字段肯定确定唯一性,那么,可以在数据库表添加唯一索引来进行标示。
8、有限状态机幂等,通过某种指定状态才能更新成下个状态。
9、类似double check机制的确认机制
悲观锁和乐观锁大部分场景下差异不大,一些独特场景下有一些差别,一般我们可以从如下几个方面来判断:
1)响应速度:如果需要非常高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不需要等待其他并发去释放锁
2)冲突频率:如果冲突频率非常高,建议采用悲观锁,保证成功率,如果冲突频率大,乐观锁会需要多次重试才能成功,代价比较大
3)重试代价:如果重试代价大,建议采用悲观锁。
3、redis分布式锁,支付完删除值,所以可以防止多次提交。
SETNX key value
将ke的值设为value,当且仅当key不存在。若给定的key已经存在,则SETNX 不做任何动作。
返回值:设置成功,返回1。设置失败,返回0。
SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。
行锁,排它锁:
for update是在数据库中上锁用的,可以为数据库中的行上一个排它锁。当一个事务的操作未完成时候,其他事务可以读取但是不能写入或更新。
如果查询不到,锁失效? 如果查询不到,同时进行插入操作,还是会插入多条重复的记录。
但是请注意如果查询范围比较大有可能会锁住整张表,确定主键索引才能使用
注意:当选中某一个行的时候,如果是通过主键id选中的,那么这个时候是行级锁。
其他行还是可以直接insert 或者update的。如果是通过其他的方式选中行,或者选中的条件不明确包含主键,这个时候会锁表。其他的事务对该表的任意一行记录都无法进行插入或者更新操作,只能读取。
锁定一行还是锁定多行,还是锁表? 这个尽量别使用,如果锁表将会导致整个项目性能低下。
删除操作:设置过期时间为2分钟,两分钟内还没请求,令牌自动失效。
幂等操作校验,可以通过注解来实现么? 完全可以封装一个注解。