如何防止重复下单

文章目录

一:重复下单与幂等性问题

用户在下单页面进行下单时,由于用户点击下单按钮 多次 、或者 重试策略导致在订单服务中接收到了 两次同样 的下单请求。

什么情况下会重复下单

  1. 用户短时间内多次点击下单按钮
  2. 超时重试
  3. 用户APP强退/闪退之后重新下单

重复下单问题,本质上,就是下单操作的幂等性问题,说到底,就是接口幂等性。

二:如何解决重复下单问题

方案一:提交订单按钮置灰

浏览器端去拦住一部分请求,减少后端服务器的处理压力,达到过滤流量的效果。

方案一优点:简单。基本可以防止重复点击提交按钮造成的重复提交问题。

方案一缺点:前进后退操作,或者F5刷新页面等问题并不能得到解决。因为服务端这里没有加限制

方案二:请求唯一ID+数据库唯一索引约束(数据库层面做幂等)

  1. 当用户进入订单提交界面的时候,调用后端获取请求唯一ID,并将唯一ID值埋点在页面里面。
  2. 当用户点击提交按钮时,后端检查这个唯一ID是否用过,如果没有用过,继续后续逻辑;如果用过,就提示重复提交。
  3. 最关键的一步操作,就是把这个唯一ID 存入业务表中,同时设置这个字段为唯一索引类型,从数据库层面做防止重复提交

缺点:DB层面约束,并发量低

方案三:redis分布式锁 + token 推荐

  1. 用户点击提交订单按钮,服务端接受到请求后,通过规则计算出本次请求唯一ID值
    • 接口唯一ID:应用名+接口名+方法名+请求参数签名(请求header、body参数,取SHA1值)
  2. 使用redis的分布式锁服务,对请求 ID 在限定的时间内尝试进行加锁,如果加锁成功,继续后续流程;如果加锁失败,说明服务正在处理,请勿重复提交。
  3. 最后一步,如果加锁成功后,需要将锁手动释放掉,以免再次请求时,提示同样的信息。

思考:同一个用户可能就是想买两次相同的产品,可能说先买了一个,刚买完发现需要两个,再买一个同一个产品,所以这时候我们设计不能不让用户买,这种情况就是说能不能对唯一ID设计上来区分这次请求,如果说不能区分,那在我们业务上redis分布式锁的时间范围这要去设计准确点,比如说一次下单到支付需要多长时间,锁的时间设置为比这个时间稍大些,不能时间太长,这样用户第二次购买发现不让重复操作。当然前面只是一种思路,还比如能不能经过判断后唯一ID相同,但是不知道用户是不是想重复购买,这时候可以给用户提示:发现购买相同,是否继续,继续那就放行,不继续就拦截。

代码实现思路:使用AOP实现对业务token的无侵入生成

可以定义注解,在生成订单的方法上加该注解,AOP的逻辑就是生成唯一token,然后加锁,看加锁能不能成功,成功则放行执行后续逻辑,失败则表示重复下单,拦截。


万能方案
一锁二判三更新

一锁:先加锁,可以加分布式锁、悲观锁都可以,但是一定是一个互斥锁

二判:进行幂等性判断,可以基于状态机业务流水表数据库唯一索引等,进行重复操作的判断。

三更新:对数据进行更新,将数据进行持久化。

三:总结

防止重复下单,本质上就是先做重复判断,然后服务端做好幂等性控制,结合实际业务场景选择相应的方案。