当前位置: 首页 > news >正文

电商业务中如何防止重复下单?

用户下单流程

我们从用户浏览商品开始,看看用户下单的简要过程:

用户下单简要过程

  • 浏览商品:用户查看商品详情
  • 加购/结算:用户可以选择直接购买商品,也可以先加入购物车,用户购买的这一步就是结算
  • 确认下单:结算完成,就进入了下单页面,提交订单,这一步就会生成一个订单,然后进入付款页面

我们可以看到,下单是发生在结算之后,下单之后,会生成唯一的订单号,接下来,客户端需要用这个订单号去完成支付。

那接下来先看看,为什么发生重复下单?

为什么会重复下单

为什么会重复下单,对于订单服务而言,就是接到了多个下单的请求,原因可能有很多,最常见的是这两种:

  • 用户重复提交
  • 网络原因导致的超时重试

重复下单原因

如何防止重复下单

防止用户提交,最常规的做法,就是客户端点击下单之后,在收到服务端响应之前,按钮置灰。

当然,防止重复下单,肯定不能只依靠客户端,可能会因为一些网络的抖动,导致仍然有重复的请求到达服务端,所以还是要在服务端做防重/幂等的处理。

PS:这里额外插入一点我对防重和幂等的理解:防重指的是防止重复提交,幂等指的是多次请求如一次,简单说,就是防重可以给对重复请求抛异常,幂等是对重复的请求响应第一次的结果,在我们讨论的这个场景里,幂等就是响应唯一的订单号。

防重和幂等

防重第一步,需要识别请求是否重复,这一步,需要客户端配合实现。

为什么呢?大家想一下,下单的时候,服务端怎么去判断这个下单请求是否唯一呢?金额?商品?优惠券?……万一用户就是喜欢,又下了一个一模一样的单呢?

所以,需要客户端在请求下单接口的时候,需要生成一个唯一的请求号:requestId,服务端拿这个请求号,判断是否重复请求。

那么,接下来,压力就给到服务端了,看看服务端怎么实现防重/幂等吧!

利用数据库实现幂等

可以在订单表t_order里添加一个字段:requestId,添加唯一索引:

唯一请求字段

这样一来,如果是重复的请求,在落库的时候就会报错,为了保证幂等性,我们可以catch住这个异常,根据requestId获取订单号,然后向客户端响应订单号。

大概的代码如下:

PlaceOrderResVO placeOrder(PlaceOrderReqVO reqVO) { try { //下单业务逻辑 …… //生成订单号 String oid=generateOid(); …… //订单落库 Order order = orderMapper.saveOrder(orderDO); //响应订单 resVO.setOid(order.getOid()); return resVO; } catch(UniqueKeyViolationException e) { // 发生了重复异常 // 根据请求号获取订单 Order order = getOrderByRequestId(reqVO.getRequestId()); resVO.setOid(order.getOid()); return resVO; } catch (Exception e) { } }

当然,这里不太好的地方是,拿异常来做业务判断。

利用Redis防重

另外一个办法,就是下单请求的时候要加锁了,通常我们的服务都是集群部署,所以一般都是用Redis实现分布式锁。

大概的逻辑:

  • 就是以requestId为维度,进行加锁,如果获取锁失败,就抛一个自定义的重复下单异常。
  • 如果获取到锁,先check一下,是否已经下单,为了提高性能,下单完成后,也把下单的结果放在Redis缓存里。

redis防重逻辑

大概的代码如下:

public PlaceOrderResVO placeOrder(PlaceOrderReqVO reqVO) { //加锁 RLock orderLock = redissonClient.getLock(RedisConstant.PLACE_ORDER_LOCK_KEY + reqVO.getRequestId()); //获取锁失败,抛出重复下单异常 if(orderLock.isExistes){ throw new OrderRepeatException(); } // 加锁 orderLock.lock(); try { //检查是否已经下单 RBucket<PlaceOrderResVO> orderCache = redissonClient.getBucket(RedisConstant.PLACE_ORDER_LOCK_KEY+reqVO.getRequestId()); if(orderCache.isExistes){ return orderCache.get(); } //下单业务逻辑 …… //落库 //订单落库 Order order = orderMapper.saveOrder(orderDO); …… //缓存结果 orderCache.put(resVO); return resVO; } } catch (Exception e) { //…… } finally { orderLock.unlock(); } return resVO; }

这里再说明一下:

  • 为什么获取不到锁的时候要抛异常呢?

因为下单里面其实还有一些其它的业务流程,比如锁库存、清优惠券……而此时,获取到锁的请求的下单流程还没有结束,下单的结果还获取不到,没法完成响应,也就没办法做幂等。

客户端,也可以根据响应的状态码,进行特殊处理,比如这个异常先不提示,但是允许用户再次点击下单按钮,来提升用户的体验。

http://www.jsqmd.com/news/411293/

相关文章:

  • 2026年热门的生物陶瓷3D打印,陶瓷3D打印设备,陶瓷3D打印代加工厂家优质品牌推荐 - 品牌鉴赏师
  • 耐腐蚀强碱柜价格多少,申佑达产品特色与价格分析 - 工业设备
  • 英伟达SONIC开源全身追踪技术,重构人形机器人控制逻辑
  • 总结山西地区缠绕膜个性化定制,费用多少钱比较合理? - 工业品网
  • 2026年电子汽车衡定制公司哪家靠谱,为你揭晓答案 - mypinpai
  • 2026年云南诚信的LV包回收,伯爵回收,名表回收公司品牌推荐名录 - 品牌鉴赏师
  • 2026年昆明评价高的剑南春回收,郎酒回收,泸州老窖特曲回收公司实力推荐 - 品牌鉴赏师
  • 为什么传统投资回报率计算未能准确?
  • 美通卡回收(方法、流程、折扣) - 京顺回收
  • 靠谱冷轧板服务商厂家,浙江老牌供应商费用怎么算? - 工业设备
  • rohs2.0检测仪哪个品牌性价比高,能满足检测需求吗 - 工业品网
  • Deepoc 具身模型开发板:机械臂扫地机的智能核心
  • 2026年新一代AI语音客服机器人厂商推荐,认准值得信赖的优质之选 - 品牌2025
  • 2026年天津靠谱的暖气片生产厂家排名,大户型选哪个品牌更合适 - 工业推荐榜
  • 门店出杯稳定:2026 全自动商用咖啡机品牌推荐指南 - 品牌2025
  • 聊聊北京地区实力强的公司股权纠纷律师,哪家口碑好服务靠谱? - 工业品牌热点
  • 计算机毕业设计之springboot比亚迪二手车交易管理系统
  • 揭秘岳阳科技职业学院是公立还是私立,选择它好不好? - 工业品牌热点
  • MLGO微算法科技利用开放量子系统,Lindbladian 模拟驱动的新一代量子微分方程算法亮相
  • 基于springboot + vue宠物商城平台网站系统(源码+数据库+文档)
  • 2026光亮铜破碎分选设备选购攻略,性能可靠品牌排名大揭秘 - 工业设备
  • 对比一圈后,更贴合继续教育的AI论文工具,千笔·专业论文写作工具 VS 学术猹
  • 盘点2026年汽车新能源升级企业,宣城好用的品牌排名 - myqiye
  • 同事嫌参数校验太丑?我掏出 SpEL Validator + IDEA 插件,直接让他闭嘴
  • C++中的Rule of Five
  • 新手必看!高性价比ELISA试剂盒品牌推荐,省钱又好用 - 包罗万闻
  • 2026黑河工控产品厂商口碑揭秘:哪些品牌值得信赖?中低压电气/施耐德电气/工控产品,工控产品生产厂家哪家好 - 品牌推荐师
  • 理解 SQL JOIN: ON 与 WHERE 的区别
  • feel导入
  • 项目管理工具哪个好?2026年项目管理软件推荐与排名,解决数据孤岛与成本痛点 - 十大品牌推荐