幂等性是什么?

Posted chen_sir_sh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了幂等性是什么?相关的知识,希望对你有一定的参考价值。

一. 幂等性:

所谓的幂等性,是分布式环境下的一个常见问题,一般是指我们在进行多次操作时,所得到的结果是一样的,即多次运算结果是一致的。
也就是说,用户对于同一操作,无论是发起一次请求还是多次请求,最终的执行结果是一致的,不会因为多次点击而产生副作用。

二. 什么情况下会产生重复提交(非幂等性)

以下几种情况会导致非幂等性的结果出现:

连续点击提交两次按钮;
点击刷新按钮;
使用浏览器后退按钮重复之前的操作,导致重复提交表单;
使用浏览器历史记录重复提交表单;
浏览器重复地HTTP请求等。
而幂等性的核心思想,其实就是保证这个接口的执行结果只影响一次,后续即便再次调用,也不能对数据产生影响,所以基于这样一个需求,我们如何解决幂等性问题呢?

三. 解决方法

1.前端js提交禁止按钮可以用一些js组件
2.使用Post/Redirect/Get模式
在提交后执行页面重定向,这就是所谓的Post-Redirect-Get (PRG)模式。简言之,当用户提交了表单后,你去执行一个客户端的重定向,转到提交成功信息页面。这能避免用户按F5导致的重复提交,而其也不会出现浏览器表单重复提交的警告,也能消除按浏览器前进和后退按导致的同样问题。
3.借助数据库操作
insert唯一索引,保证插入的数据只有一条。另外也可以基于悲观锁或者乐观锁,先查询后判断,首先通过查询数据库是否存在数据,如果存在证明已经请求过了,直接拒绝该请求;如果没有存在,就证明是第一次进来,直接放行。
4.session机制(后台服务端)在服务器端,生成一个唯一的标识符,将它存入session,同时将它写入表单的隐藏字段中,然后将表单页面发给浏览器,用户录入信息后点击提交。另外在服务器端,获取表单中隐藏字段的值,与session中的唯一标识符比较,如果相等说明是首次提交,就处理本次请求,然后将session中的唯一标识符移除,如果不相等即重复提交。
5.Redis token机制
每次接口请求前先获取一个token,然后再下次请求的时候在请求的header体中加上这个token,后台进行验证。如果验证通过删除token,下次请求再次判断token是否相等,如果不相等即重复提交。

概念篇嘛是幂等性?


【引言】

今天被问到一个问题,数据库中哪些操作具有幂等性。恩?当时听了很迷瞪,平时管理数据库,一些操作也没碰到幂等性这个说法啊。鉴于此,今天学习下幂等性是个嘛?!

 

【大纲】

1. 幂等性是个啥?

2. 幂等性有什么用?

3. 怎样保证幂等性?

4. 幂等性有啥不足?

 

一、嘛是幂等性?

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

 

幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。

 

幂等性衍生到软件工程中, 它的语义是指: 函数/接口可以使用相同的参数重复执行, 不应该影响系统状态, 也不会对系统造成改变

 

在编程中,一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。第一次请求的时候对资源产生了副作用,但是以后的多次请求都不会再对资源产生副作用。这里的副作用是不会对结果产生破坏或者产生不可预料的结果。

 

HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。

 

总的来说,幂等性是指:

任意多次执行对资源本身所产生的影响均与一次执行的影响相同。

 

Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

 

幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击(调用)而产生了副作用。即幂等性=多次执行结果一致+无副作用

 

举例:

数据库DML操作:

查询(select)方法本身就是幂等性的,虽然多次执行可能返回结果不一致,但是没有任何副作用。

插入(insert)和修改(update)方法是非幂等性的,需要通过机制在需要的场景处理以确保多次执行无副作用。

删除(delete)执行一次或多次都是结果为空(即结果一致),并且无副作用,所以在根据主键ID删除可以认为是(伪)幂等性的,根据非主键删除的如果多次执行无副作用(都是把数据删除),也可以认为是(伪)幂等性。

 

【概念篇】嘛是幂等性?

幂等性需关注几个重点:

1. 幂等不仅仅只是一次(或多次)请求对资源没有副作用(比如查询数据库操作,没有增删改,因此没有对数据库有任何影响)。

2. 幂等还包括第一次请求的时候对资源产生了副作用,但是以后的多次请求都不会再对资源产生副作用。

3. 幂等关注的是以后的多次请求是否对资源产生的副作用,而不关注结果。

4. 网络超时等问题,不是幂等的讨论范围。

注意:

幂等性是系统服务对外一种承诺(而不是实现),承诺只要调用接口成功,外部多次调用对系统的影响是一致的。声明为幂等的服务会认为外部调用失败是常态,并且失败之后必然会有重试。

 

二、幂等性有什么用?


什么情况下需要保证幂等性

业务开发中,经常会遇到重复提交的情况,无论是由于网络问题无法收到请求结果而重新发起请求,或是前端的操作抖动而造成重复提交情况。 在交易系统,支付系统这种重复提交造成的问题有尤其明显,比如:

1. 用户在APP上连续点击了多次提交订单,后台应该只产生一个订单;

2. 向支付宝发起支付请求,由于网络问题或系统BUG重发,支付宝应该只扣一次钱。 

很显然,声明幂等的服务认为,外部调用者会存在多次调用的情况,为了防止外部多次调用对系统数据状态的发生多次改变,将服务设计成幂等。

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

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

1. 首先去查询上一次的执行状态,如果没有则认为是第一次请求

2. 在服务改变状态的业务逻辑前,保证防重复提交的逻辑

三、怎样保证幂等性?

1. 建立唯一索引,防止新增脏数据 

可限制重复插入数据,当重复时,数据库会抛异常,保证不会出现脏数据。但体验不好,并且实用场景有限制。使用全局唯一ID,就是根据业务的操作(业务类型)和内容生成一个全局ID,在执行操作前先根据这个全局唯一ID是否存在,来判断这个操作是否已经执行。如果不存在则把全局ID,存储到存储系统中,比如数据库、redis等。如果存在则表示该方法已经执行,这个全局ID有时效性。

 

2. 利用 token 机制,防止页面重复提交 

核心思想是为每一次操作生成一个唯一性的凭证,也就是token。一个token在操作的每一个阶段只有一次执行权,一旦执行成功则保存执行结果。对重复的请求,返回同一个结果。

 

3. 状态机幂等 

在有状态的数据中可以使用,如状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。如果状态是顺序的,不可逆,那么就不会出现 ABA 问题,否则会出现 ABA问题。

 

4. 乐观锁

如果只是更新已有的数据,没有必要对业务进行加锁,设计表结构时使用乐观锁,一般通过version来做乐观锁,这样既能保证执行效率,又能保证幂等。乐观锁存在失效的情况,就是常说的ABA问题,不过如果version版本一直是自增的就不会出现ABA的情况。

 

5. 对外提供幂等的接口 

通过 source来源+seq序列号来判断请求是否重复, 在并发时只能处理一个请求。其它相同并发请求要么返回请求重复,要么等待前面请求执行完成在执行。

 

6. 防重表

使用订单号orderNo做为去重表的唯一索引,每次请求都根据订单号向去重表中插入一条数据。第一次请求查询订单支付状态,当然订单没有支付,进行支付操作,无论成功与否,执行完后更新订单状态为成功或失败,删除去重表中的数据。后续的订单因为表中唯一索引而插入失败,则返回操作失败,直到第一次的请求完成(成功或失败)。可以看出防重表作用是加锁的功能。

 

7. select + insert 

这种情况在没有并发的系统中可以解决幂等问题,在单JVM有并发的时候可以加锁来保证幂等性,在分布式环境它是无保证幂等的,这时候需要用到分布式锁来保证。

 

8. 分布式锁

在进入方法时,先去获取锁,假如获取到锁,就继续后面的流程。假如没有获取到锁,就等待锁的释放直到获取到锁。当执行完方法时,释放锁。当然,锁要设个超时时间,防止意外没有释放到锁。它用来解决分布式系统的幂等性,常用的实现方案是 redis zookeeper 等工具。比如Redis。订单发起支付请求,支付系统会去Redis缓存中查询是否存在该订单号的Key,如果不存在,则向Redis增加Key为订单号。查询订单支付已经支付,如果没有则进行支付,支付完成后删除该订单号的Key。通过Redis做到了分布式锁,只有这次订单订单支付请求完成,下次请求才能进来。相比去重表,将放并发做到了缓存中,较为高效。思路相同,同一时间只能完成一次支付请求。 

 

9. 支付缓冲区

把订单的支付请求都快速地接下来,一个快速接单的缓冲管道。后续使用异步任务处理管道中的数据,过滤掉重复的待支付订单。优点是同步转异步,高吞吐。不足是不能及时地返回支付结果,需要后续监听支付结果的异步返回。

 

四、 幂等性有啥缺点?

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

 

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

 

最后,幂等性虽然复杂化了业务功能和降低了执行效率,但为了保证系统的正确性,是必要的。就上面更新 X 的例子,在单台服务器上,给那段代码加上锁,并给 X 设为 volatile,就保证来数据的正确性了。在分布式环境下并且 X 是从数据库或者文件里查询出来的,用上面加锁的方式实现就不能保证数据的正确性了,这时候就需要用到分布式锁了。所以,保证方法或接口的幂等性是非常有必要的,因为数据是不能出现任何问题的。

 

五、幂等VS防重

重复提交是在第一次请求已经成功的情况下,人为的进行多次操作,导致不满足幂等要求的服务多次改变状态。而幂等更多使用的情况是第一次请求不知道结果(比如超时)或者失败的异常情况下,发起多次请求,目的是多次确认第一次请求成功,却不会因多次请求而出现多次的状态变化。

可见,幂等性和重复提交的共同点是均发生了多次提交;不同是幂等性的请求改变只有一次,重复提交的状态会发生变化。


【参考】

https://www.cnblogs.com/javalyy/p/8882144.html

【参考】

https://www.jianshu.com/p/9d46a730284e

【参考】

https://blog.csdn.net/mulinsen77/article/details/89051765

【参考】

https://blog.csdn.net/suchahaerkang/article/details/83623764

【参考】

https://segmentfault.com/a/1190000019529787

【参考】

https://blog.csdn.net/rentuo53/article/details/84919526



往期精彩文章

========================================










以上是关于幂等性是什么?的主要内容,如果未能解决你的问题,请参考以下文章

概念篇嘛是幂等性?

谈谈幂等性

幂等性

幂等性

RabbitMQ消息幂等性问题

理解http的幂等性