- 什么是幂等性
- 一、什么情况下会产生幂等性问题
- 二、幂等解决方案
- 2.1 前端页面控制
- 2.1.1 设置按钮只可操作一次
- 2.1.2 使用Post/Redirect/Get模式
- 2.1.3 在session存放特殊标志
- 2.2 服务端校验
- 2.2.1 使用分布式锁实现幂等性
- 2.2.2 数据库机制
- 2.2.2.1 状态机幂等
- 2.2.2.2 乐观锁
- 2.2.2.3 使用唯一键来约束
- 2.2.3 缓冲队列
什么是幂等性
幂等性原本是数学上的概念,用在接口上就可以理解为用户对于同一操作发起的一次请求或者多次请求的结果是一致的,就相等于只执行了一次,不会因为多次点击而产生了副作用。
一、什么情况下会产生幂等性问题那么,什么情况下,会产生接口幂等性的问题呢?
- 网络波动, 可能会引起重复请求
- 用户重复操作,用户在操作时候可能会无意触发多次下单交易,甚至没有响应而有意触发多次交易应用
- 使用了失效或超时重试机制(Nginx重试、RPC重试或业务层重试等)
- 页面重复刷新
- 使用浏览器后退按钮重复之前的操作,导致重复提交表单
- 使用浏览器历史记录重复提交表单
- 浏览器重复的HTTP请求
- 定时任务重复执行
- 用户双击提交按钮等等
解决办法分为两个方向,一个方向是客户端防止重复调用,一个是服务端进行校验。当然,客户端防止重复提交并不是绝对可靠的,优点是实现起来比较简单。
2.1 前端页面控制 2.1.1 设置按钮只可操作一次一般是提交后把按钮置灰或loding状态,消除用户因为重复点击而产生的重复记录,比如添加操作,由于点击两次而产生两条记录
2.1.2 使用Post/Redirect/Get模式在提交后执行页面重定向,这就是所谓的Post-Redirect—Get(PRG)模式,简单来说就是当用户提交连表单后,跳转到一个重定向的信息页面,这样就避免用户按F5刷新导致的重复提交,而且也不会出现浏览器表单重复提交的警告,也能消除按浏览器前进和后退导致同样重复提交的问题。
2.1.3 在session存放特殊标志在服务端,生成一个唯一的标识符,将它存入session,同时前端获取这个标识符的值将它写入表单的隐藏中,用于用户输入信息后点击一起提交,在服务器端,获取表单中隐藏字段的值,与session中的唯一标识符比较,相等说明是首次提交,就处理本次请求,然后将session中的唯一标识符移除,不相等则表示是重复提交,不再做处理。
2.2 服务端校验 2.2.1 使用分布式锁实现幂等性Redis实现的方式就是将唯一序列号作为Key,value可以是你想填的任何信息。唯一序列号也可以是一个字段,例如订单的订单号,也可以是多字段的唯一性组合。当然这里需要设置一个 key 的过期时间,否则 Redis 中会存在过多的 key,每次处理数据,先看这个key是否已经存在,存在就不处理。
2.2.2 数据库机制 2.2.2.1 状态机幂等如果业务上需要修改订单状态,例如订单状态有待支付,支付中,支付成功,支付失败。设计时最好只支持状态的单向改变。这样在更新的时候就可以加上条件,多次调用也只会执行一次。例如想把订单状态更新为支持成功,则之前的状态必须为支付中
update table_name set status = 支付成功 where id=id值 and status = 支付中2.2.2.2 乐观锁
- 查询数据获得版本号
- 通过版本号去更新,版本号匹配则更新,版本号不匹配则不更新
-- 假如查询出的version为1 select version from table_name where userid = 10; -- 给用户的账户加10 update table_name set money = money -10, version = version + 1 where userid = 10 and version = 12.2.2.3 使用唯一键来约束
利用数据库唯一索引机制,当数据重复时,插入数据库会抛出异常,保证不会出现脏数据。
2.2.3 缓冲队列将请求都快速地接收下来后放入缓冲队列中,后续使用异步任务处理队列中的数据,过滤掉重复的请求,该解决方案优点是同步处理改成异步处理、高吞吐量,缺点则是不能及时地返回请求结果,需要后续轮询得处理结果。
参考
如何保证接口的幂等性?常见的实现方案有哪些?
如何保证接口的幂等性?



