• Welcome to the world's largest Chinese hacker forum

    Welcome to the world's largest Chinese hacker forum, our forum registration is open! You can now register for technical communication with us, this is a free and open to the world of the BBS, we founded the purpose for the study of network security, please don't release business of black/grey, or on the BBS posts, to seek help hacker if violations, we will permanently frozen your IP and account, thank you for your cooperation. Hacker attack and defense cracking or network Security

    business please click here: Creation Security  From CNHACKTEAM

告诉我你们公司的系统是如何防止重复数据的?


Recommended Posts

博主项目报错,用户操作失败。在我们的设计中,操作回滚是返回到操作前的状态。查日志发现用户之前的操作已经做了两次,也就是说提交操作的接口已经被调用了两次,导致用户上次和这次的状态是一样的,所以操作回滚是没有问题的。问题在于操作的接口被调用了两次。

为防止重复提交,在前端进行控制。用户点击按钮后,后台返回成功结果,按钮不可见。实践证明,客户端的限制操作并不是绝对可靠的。

针对以上场景,引入今天的问题。什么是接口幂等?如何保证接口的幂等性?

什么是接口幂等性?

先看幂等的概念:

幂等原本是一个数学概念。在接口中使用时,可以理解为:* *对于同一个接口,同一个请求多次发出,操作必须保证只执行一次。* *当调用接口异常,反复尝试时,总会造成系统无法承受的损失,这种现象一定要防止。

比如以下几种情况,如果没有实现接口幂等,就会产生严重后果:支付接口,重复支付会导致多扣;订单界面,同一订单可能会创建多次。

为什么会产生接口幂等性问题?

那么,什么情况下会出现接口幂等的问题呢?

网络波动可能会导致重复请求。

如果用户重复操作,用户可能会无意中触发多个订单交易,甚至有意触发多个交易申请而没有响应。

失败或超时重试机制(Nginx重试、RPC重试或业务层重试等。)被使用。

重复刷新页面

使用浏览器的后退按钮重复前面的操作,导致表单重复提交。

使用浏览器历史重复提交表单

来自浏览器的重复HTTP请求

重复执行计划任务

用户双击提交按钮。

如何保证接口幂等性?

所以最重要的是,如何保证接口的幂等性?

解决方案分两个方向,一个是客户端防止重复调用,一个是服务器检查。当然,对于客户端来说,防止重复提交并不是绝对可靠的。优点是实现起来相对简单。

1.按钮只可操作一次

一般提交后按钮会灰显或者loding,以消除用户重复点击导致的重复记录,比如添加操作,由于两次点击导致两次记录。

2.token机制

功能上允许重复提交,但要保证重复提交不会产生副作用。例如,单击N次只会产生一条记录。具体实现就是在进入页面的时候申请一个令牌,然后后续所有的请求都会取这个令牌,后端会根据令牌避免重复请求。

kopumkpc0d14300.png

3.使用Post/Redirect/Get模式

页面重定向是在提交之后进行的,也就是所谓的Post-Redirect-Get (PRG)模式。简单来说,当用户提交一个链接的表单时,跳转到一个重定向的信息页面,从而避免了用户按F5刷新导致的重复提交,避免了浏览器表单重复提交的警告,消除了浏览器前后按导致的相同重复提交的问题。

4.在session存放特殊标志

在服务器端,生成一个惟一的标识符,并存储在会话中。同时,前端获取该标识符的值,并将其写入

用户输入信息后点击一起提交,在服务器端,获取表单中隐藏字段的值,与session中的唯一标识符比较,相等说明是首次提交,就处理本次请求,然后将session中的唯一标识符移除,不相等则表示是重复提交,不再做处理。

5.使用唯一索引防止新增脏数据

利用数据库唯一索引机制,当数据重复时,插入数据库会抛出异常,保证不会出现脏数据。

6.乐观锁

如果更新已有数据,可以进行加锁更新,也可以设计表结构时使用乐观锁,通过version来做乐观锁,这样既能保证执行效率,又能保证幂等, 乐观锁的version版本在更新业务数据要自增
update table set version = version + 1 where id = #{id} and version = #{version}
示例: 当有重复请求的时候,第一个请求会获取当前商品的version版本号,得到的version为1,紧接着由于第一个请求还没更新商品的version,第二个请求获取的version依然也是1, 这时候第一个请求操作更新的时候带上version并作为条件并且自增更新,这时候商品的version就会变成2,当第二个请求去操作更新的时候明显version不一致导致更新失败。

7.select + insert or update or delete

该方案就是操作之前先查询一下,符合要求再插入,该方案在没有并发的系统中可以解决幂等问题,在单JVM有并发的时候可以用JVM加锁来保证幂等性,在分布式环境它是无法保证幂等性,可以使用分布式来保证。

8.分布式锁

如果是分布是系统,构建全局唯一索引比较困难,例如唯一性的字段没法确定,这时候可以引入分布式锁,通过第三方的系统(redis或zookeeper),在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁,这样其实是把多线程并发的锁的思路,引入多多个系统,也就是分布式系统中得解决思路。要点:某个长流程处理过程要求不能并发执行,可以在流程执行之前根据某个标志(用户ID+后缀等)获取分布式锁,其他流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行成功,执行完成后,释放分布式锁(分布式锁要第三方系统提供)。

9.状态机幂等

在设计单据相关的业务,或者是任务相关的业务,肯定会涉及到状态机(状态变更图),就是业务单据上面有个状态,状态在不同的情况下会发生变更,一般情况下存在有限状态机,这时候,如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。注意:订单等单据类业务,存在很长的状态流转,一定要深刻理解状态机,对业务系统设计能力提高有很大帮助 。

10.防重表

以支付为例: 使用唯一主键去做防重表的唯一索引,比如使用订单号作为防重表的唯一索引,每一次请求都根据订单号向防重表中插入一条数据,插入成功说明可以处理后面的业务,当处理完业务逻辑之后删除防重表中的订单号数据,后续如果有重复请求,则会因为防重表唯一索引原因导致插入失败,直接返回操作失败,直到第一次请求返回结果,可以看出防重表作用就是加锁的功能。
注: 最好结合状态机幂等先判断一下

11.缓冲队列

将请求都快速地接收下来后放入缓冲队列中,后续使用异步任务处理队列中的数据,过滤掉重复的请求,该解决方案优点是同步处理改成异步处理、高吞吐量,缺点则是不能及时地返回请求结果,需要后续轮询得处理结果。

12.全局唯一号

比如通过source来源 + 唯一序列号传入给后端,后端来判断请求是否重复,在并发时只能处理一个请求,其他相同并发请求要么返回请求重复,要么等待 前面请求执行完成后再执行。

参考:

【1】:什么是接口的幂等性,如何实现接口幂等性?一文搞定
【2】:分布式系统中接口的幂等性
【3】:高并发下接口幂等性解决方案

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now