什么是幂等
幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。
在编程中,幂等 的理解就是,在数据不变的情况下,一个操作,无论执行多少次,结果都是一样的
常用场景
-
前端数据重复提交
-
订单支付请求
无论是网络超时?系统bug?还是其他原因,都只应该扣一次钱
-
Mpush推送消息
推送多条同样的消息,用户会疯掉的
-
发短信给用户
-
用户下单
等等,很多熟悉的应用场景,都需要考虑幂等性,而导致数据不一致或者出问题的情况很多,
例如:重复点击? 重复提交?网络重发?或者nginx重发导致的数据重复提交等等
幂等技术方案
查询操作
查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作
删除操作
删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个)
唯一索引
数据库创建唯一索引或者组合唯一索引,都可以避免脏数据,保证幂等,但是注意处理异常
token机制
之前在做接口开发的时候,思考过几个问题:
- 如何保证请求接口响应结果的唯一性
- 如何防止恶意请求或者攻击
- 分布式环境下,如何保证请求接口的响应结果
当时考虑的结果是用token + redis来实现的,流程如下:
- 请求之前,先问服务器要一个token,服务器缓存token到redis,加上超时时间
- 请求的时候,带着token
- 服务器从redis获取token校验,通过后,处理,响应,删除token,未通过校验,则提示token无效
注意:
- token在redis的超时时间合理控制,防止恶意请求
- 服务器校验token的时候,采用
redis.delete
操作,直接使用get+delete 容易存在并发问题 - token在一定程度上可以限流
悲观锁
悲观锁,获取数据,用的不多,主要使用的不读,容易锁表。
select * from table_xxx where id='xxx' for update;
注意:
- id字段一定是主键或者唯一索引,不然是锁表
- 悲观锁一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用
乐观锁
类似于CAS,也是compareAndSet
操作,在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高,常用的就是加版本号控制,或者根据唯一条件。
update t_user set name='123',ver=ver+1 where ver='1.2.3'
说起版本号,突然想起CAS的ABA问题------AtomicStampedReference的解决方案
分布式锁
数据处理在分布式环境下一向是很很麻烦的,因为Synchronize
和ReentrantLock
在分布式环境下是达不到并发锁的效果的。所以分布式锁,一般都采用第三方,例如,redis,zookeeper,就好比,去第三方认证获取一个permit
,处理完了就去注销这个permit
,确保同一时刻,只有一个能执行成功
分布式环境下,经常还需要考虑一个全局唯一索引的问题,常用解决方案如下:
- 雪花算法
- 令牌桶算法
- 漏桶算法
- 自定义唯一索引生成器
select + insert
在一些小型系统或者并发不高的项目里面,防止重复执行,可以采用select
+insert
状态机
如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。
在幂等的处理机制中,状态机是个很有用的东东,已经确定的状态不会回滚处理,比如订单业务里面,订单是已支付状态,再来这个订单的支付请求,肯定是不行的嘛,怼回去。
很多场景或者系统中都用到状态机,所以设计合理的状态机制,是很有用且很必要的。
总结
在接口开发,特别是和银行支付相关的项目中,幂等是首要考虑的问题,也是在面试中文的比较多的一个问题点,因此理解什么是幂等,如何保证幂等是很总要的。
参考 https://mp.weixin.qq.com/s/aqlWMxnneFhSIFJBPMW25A
本文大部分参考以上微信公众号的文章,如有侵权,请联系删除