幂等性问题和解决方法

在实际的开发项目中,一个对外暴露的接口往往会面临很多次请求。这就需要考虑到一个幂等性问题。

幂等性

幂等性的概念是:任意多次执行所产生的影响均与一次执行的影响相同,即无论你请求了多少次,对数据库的影响都只能有一次,不能重复处理。

所以,按照上面的理解,每次执行的结果都会发生变化,就是非幂等的。如下面三条sql,只有第三条是非幂等的。

SELECT col1 FROM tab1 WHER col2=2,无论执行多少次都不会改变状态,是天然的幂等。

UPDATE tab1 SET col1=1 WHERE col2=2,无论执行成功多少次状态都是一致的,因此也是幂等操作。

UPDATE tab1 SET col1=col1+1 WHERE col2=2,每次执行的结果都会发生变化,这种不是幂等的。

http和幂等性

这里就可以牵扯到http请求了。http请求的get方法是幂等性的,因为不会对数据库造成更改影响,每次返回的数据都一致。而post请求是非幂等的,因为每请求一次都会添加一条数据

(按restful规范,post常规是添加数据时使用。当然,也有例外,我们有的时候可能需要把查询方法改造成 POST 方法。比如,超长(1k)的 GET URL 使用 POST 方法来替代,因为 GET 受到 URL 长度的限制。虽然,它不符合幂等性,但是它是一种折中的方案。)

因此 根据restful的规范,http的请求会有以下是否幂等性的判断。

Http method 操作 是否幂等
get 获取数据
post 新建数据 ×
put 更新所有数据
patch 更新部分数据 ×
delete 删除资源

为什么要设计幂等性的服务

幂等可以使得客户端逻辑处理变得简单,但是却以服务逻辑变得复杂为代价。满足幂等服务的需要在逻辑中至少包含两点:

  1. 首先去查询上一次的执行状态,如果没有则认为是第一次请求
  2. 在服务改变状态的业务逻辑前,保证防重复提交的逻辑

幂等的不足

幂等是为了简化客户端逻辑处理,却增加了服务提供者的逻辑和成本,是否有必要,需要根据具体场景具体分析,因此除了业务上的特殊要求外,尽量不提供幂等的接口。

  1. 增加了额外控制幂等的业务逻辑,复杂化了业务功能;
  2. 把并行执行的功能改为串行执行,降低了执行效率。

幂等性解决方案

  1. 防重表
    数据库建立唯一性索引,可以保证最终插入数据库的只有一条数据(比如订单表对订单号进行唯一索引,所有重复提交可能产生同一个订单号的都会被拆除。当然,订单号要按你自己的设定走,一般订单号设计会是时间戳加迭代。那么如果是这样,创建仍然不能保证幂等,具体根据业务需求来判定建立的唯一索引位置)
  2. token令牌机制,
    分为两个阶段,获取token和使用token。每次接口请求前先获取一个token,然后再下次请求的时候在请求的header体中加上这个token,后台进行验证,如果验证通过删除token,下次请求再次判断token。如果使用上redis缓存,流程图会是这样。在这里插入图片描述
  3. 先查询后判断,
    首先通过查询数据库是否存在数据,如果存在证明已经请求过了,直接拒绝该请求,如果没有存在,就证明是第一次进来,直接放行。
  4. 支付缓冲区
    把订单的支付请求都快速地接下来,一个快速接单的缓冲管道。后续使用异步任务处理管道中的数据,过滤掉重复的待支付订单。优点是同步转异步,高吞吐。不足是不能及时地返回支付结果,需要后续监听支付结果的异步返回。(一般支付都是采用这种方式)
  5. 悲观锁或者乐观锁。
    悲观锁可以保证每次for update的时候其他sql无法update数据(在数据库引擎是innodb的时候,select的条件必须是唯一索引,防止锁全表)
    乐观锁,一般通过version来做乐观锁,这样既能保证执行效率,又能保证幂等。例如: UPDATE tab1 SET col1=1,version=version+1 WHERE version=#version# 不过,乐观锁存在失效的情况,就是常说的ABA问题,不过如果version版本一直是自增的就不会出现ABA的情况。(ABA可以查看这篇文章链接,关于CAS机制可以看这篇链接
  6. 分布式锁
    防重表可以使用分布式锁代替,比如Redis。订单发起支付请求,支付系统会去Redis缓存中查询是否存在该订单号的Key,如果不存在,则向Redis增加Key为订单号。查询订单支付已经支付,如果没有则进行支付,支付完成后删除该订单号的Key。通过Redis做到了分布式锁,只有这次订单订单支付请求完成,下次请求才能进来。相比去重表,将放并发做到了缓存中,较为高效。思路相同,同一时间只能完成一次支付请求。
发布了58 篇原创文章 · 获赞 8 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_32020035/article/details/105448889