幂等的理解和处理

什么是幂等

幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。

在编程中,幂等 的理解就是,在数据不变的情况下,一个操作,无论执行多少次,结果都是一样的

常用场景

  • 前端数据重复提交

  • 订单支付请求

    无论是网络超时?系统bug?还是其他原因,都只应该扣一次钱

  • Mpush推送消息

    推送多条同样的消息,用户会疯掉的

  • 发短信给用户

  • 用户下单

等等,很多熟悉的应用场景,都需要考虑幂等性,而导致数据不一致或者出问题的情况很多,

例如:重复点击? 重复提交?网络重发?或者nginx重发导致的数据重复提交等等

幂等技术方案

查询操作

查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作

删除操作

删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个)

唯一索引

数据库创建唯一索引或者组合唯一索引,都可以避免脏数据,保证幂等,但是注意处理异常

token机制

之前在做接口开发的时候,思考过几个问题:

  1. 如何保证请求接口响应结果的唯一性
  2. 如何防止恶意请求或者攻击
  3. 分布式环境下,如何保证请求接口的响应结果

当时考虑的结果是用token + redis来实现的,流程如下:

  1. 请求之前,先问服务器要一个token,服务器缓存token到redis,加上超时时间
  2. 请求的时候,带着token
  3. 服务器从redis获取token校验,通过后,处理,响应,删除token,未通过校验,则提示token无效

注意

  1. token在redis的超时时间合理控制,防止恶意请求
  2. 服务器校验token的时候,采用redis.delete操作,直接使用get+delete 容易存在并发问题
  3. token在一定程度上可以限流

悲观锁

悲观锁,获取数据,用的不多,主要使用的不读,容易锁表。

select * from table_xxx where id='xxx' for update;

注意

  1. id字段一定是主键或者唯一索引,不然是锁表
  2. 悲观锁一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用

乐观锁

类似于CAS,也是compareAndSet操作,在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高,常用的就是加版本号控制,或者根据唯一条件。

update t_user set name='123',ver=ver+1 where ver='1.2.3' 

说起版本号,突然想起CAS的ABA问题------AtomicStampedReference的解决方案

分布式锁

​ 数据处理在分布式环境下一向是很很麻烦的,因为SynchronizeReentrantLock在分布式环境下是达不到并发锁的效果的。所以分布式锁,一般都采用第三方,例如,redis,zookeeper,就好比,去第三方认证获取一个permit,处理完了就去注销这个permit确保同一时刻,只有一个能执行成功

​ 分布式环境下,经常还需要考虑一个全局唯一索引的问题,常用解决方案如下:

  1. 雪花算法
  2. 令牌桶算法
  3. 漏桶算法
  4. 自定义唯一索引生成器

select + insert

在一些小型系统或者并发不高的项目里面,防止重复执行,可以采用select+insert

状态机

​ 如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。

​ 在幂等的处理机制中,状态机是个很有用的东东,已经确定的状态不会回滚处理,比如订单业务里面,订单是已支付状态,再来这个订单的支付请求,肯定是不行的嘛,怼回去。

​ 很多场景或者系统中都用到状态机,所以设计合理的状态机制,是很有用且很必要的。

总结

​ 在接口开发,特别是和银行支付相关的项目中,幂等是首要考虑的问题,也是在面试中文的比较多的一个问题点,因此理解什么是幂等,如何保证幂等是很总要的。

参考 https://mp.weixin.qq.com/s/aqlWMxnneFhSIFJBPMW25A
本文大部分参考以上微信公众号的文章,如有侵权,请联系删除

你可能感兴趣的:(java)