如何防止订单重复提交
- 前言
- 什么是重复下单
- 为什么会重复下单?如何处理重复下单?
- 利用数据库实现幂等
- 利用Redis防重
前言
最近在做电商项目,整理一下解决方案并帮助自己巩固知识点,此方案是结合了目前的业务环境,若有更好的解决的方式很高兴与大家一起讨论。
什么是重复下单
首先我们要了解一下下单流程,浏览商品,下单结算,确认下单。
浏览
下单
提交订单结算
我也不买这个。
- 浏览商品:用户查看商品详情
- 加购/结算:用户可以选择直接购买商品,也可以先加入购物车,用户购买的这一步就是结算
- 确认下单:结算完成,就进入了下单页面, 提交订单,这一步就会生成一个订单,然后进入付款页面
我们可以看到,下单是发生在结算之后,下单之后,会生成唯一的订单号,接下里,客户端需要用这个订单号去完成支付。
那接下来先看看,为什么发生重复下单?
为什么会重复下单?如何处理重复下单?
- 用户重复提交
- 网络原因导致的超时重试
在网上看别人解决方案,贴张图(借鉴一下别人的图)
重复下单情况
理想下单情况
防止用户提交,最常规的做法,就是客户端点击下单之后,在收到服务端响应之前,按钮置灰。
当然,防止重复下单,肯定不能只依靠客户端,可能会因为一些网络的抖动,导致仍然有重复的请求到达服务端,所以还是要在服务端做防重/幂等的处理。
PS:这里额外插入一点我对防重和幂等的理解:防重指的是防止重复提交,幂等指的是多次请求如一次,简单说,就是防重可以给对重复请求抛异常,幂等是对重复的请求响应第一次的结果,在我们讨论的这个场景里,幂等就是响应唯一的订单号。
防重第一步,需要识别请求是否重复,这一步,需要客户端配合实现。
为什么呢?大家想一下,下单的时候,服务端怎么去判断这个下单请求是否唯一呢?金额?商品?优惠券?……万一用户就是喜欢,又下了一个一摸一样的单呢?
所以,需要客户端在请求下单接口的时候,需要生成一个唯一的请求号:requestId,服务端拿这个请求号,判断是否重复请求。
那么,接下来,压力就给到服务端了,看看服务端怎么实现防重/幂等吧!
利用数据库实现幂等
可以在订单表t_order里添加一个字段:requestId,添加唯一索引:
这样一来,如果是重复的请求,在落库的时候就会报错,为了保证幂等性,我们可以catch住这个异常,根据requestId获取订单号,然后向客户端响应订单号。
利用Redis防重
另外一个办法,就是下单请求的时候要加锁了,通常我们的服务都是集群部署,所以一般都是用Redis实现分布式锁。
当用户下单时,去Redis中获取一个锁,即一个标识,同一时间只能有一个人能获取到。获取到之后才可以进行后续的逻辑,例如清除购物车、标记消费券等。若此时另外一个下单请求突破防线进入了系统,首先它需要去Redis获取锁,然而锁已经被占用了,它获取不到直接就返回失败。这里主要保证的是接口幂等性。
这个也相当于是个令牌桶的思路。
小结
- 引入文章1:https://blog.csdn.net/xiao_bai_9527/article/details/127468404
- 引入文章2:https://blog.csdn.net/sinat_40770656/article/details/126077217