面试官:如何防止重复提交订单?

面试官:如何防止重复提交订单?

  • 这个问题,在电商领域的面试场景题下,应该算是妥妥的高频问题了,仅次于所谓的“秒杀场景如何实现”。
  • 说个题外话,有段时间“秒杀场景如何实现”这个问题风靡一时,甚至在面试的时候,有些做财务领域、OA领域公司的面试官也都跟风问。
  • 大有一种”无秒杀,不面试“的感觉了。

重复提交原因

  • 其实原因无外乎两种:
    • 一种是由于用户在短时间内多次点击下单按钮,或浏览器刷新按钮导致。
    • 另一种则是由于Nginx或类似于SpringCloud Gateway的网关层,进行超时重试造成的。

常见解决方案

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

  • 这种解决方案在注册登录的场景下比较常见,当我们点击”发送验证码“按钮的时候,会进行手机短信验证码发送,且按钮就会有一分钟左右的置灰。

  • 有些经验不太丰富的同学,通常会简单粗暴地把这个方案直接照搬过来。

  • 但这种方案只能解决多次点击下单按钮的问题,对于Nginx或类似于SpringCloud Gateway的超时重试所导致的问题是无能为力的。

  • 当然,这种方案也不是真的没有价值。它可以在高并发场景下,从浏览器端去拦住一部分请求,减少后端服务器的处理压力。

  • 说到底,“下单防重”的问题是属于“接口幂等性”的问题范畴。

  • 面试官:如何防止重复提交订单?_第1张图片

  • 幂等性

  • 接口幂等性是指:以相同的参数,对一个接口进行多次调用,所产生的结果和一次调用是完全相同的。

  • 下面的情况就是幂等的:

  • student.setName("张三");
    
  • 而这种情况就是非幂等的,因为每次调用,年龄都会增加一岁。

  • student.increaseAge(1);
    
  • 现在我们的思路需要切换到幂等性的解决方案来。

  • 同样是幂等性场景,“如何防止重复提交订单” 比 “如何防止订单重复支付” 的解决方案要难一些。

  • 因为,后者在常规情况下,一个订单都是对应一笔支付单,所以orderID可以作为一个幂等性校验、防止订单重复支付的天然神器。

  • 但这个方案在“如何防止重复提交订单”就不适用了,需要其他的解决方案,请继续看下文。

方案二:预生成全局唯一订单号

  • 后端新增一个接口,用于预生成一个“全局唯一订单号”,如:UUID 或 NanoID。

  • 进入创建订单页面时,前端请求该接口,获取该订单号。

  • 在提交订单时,请求参数里要带上这个预生成的“全局唯一订单号”,利用数据库的唯一索引特性,在插入订单记录时,如果该“全局唯一的订单号”重复,记录会插入失败。

  • btw:该“全局唯一订单号”不能代替数据库主键,在未分库分表场景下,主键还是用数据库自增ID比较好。

  • 面试官:如何防止重复提交订单?_第2张图片

  • 方案二

  • 优点:彻底解决了重复下单的问题;

  • 缺点:方案复杂,前后端都有开发工作量,还要新增接口,新增字段。

  • 另外,网上还有同学说,要单独弄一个生成“全局唯一订单号”的服务,我觉得还是免了吧,这不是更麻烦了吗?

方案三:前端生成全局唯一订单号

  • 这种方案是在借鉴了“方案二”的基础上,做了一些实现逻辑的简化。

  • 用户进入下页面时,前端程序自己生成一个“全局唯一订单号”。

  • 在提交订单时,请求参数里要带上这个预生成的“全局唯一订单号”,利用数据库的唯一索引特性,在插入订单记录时,如果该“全局唯一的订单号”重复,记录会插入失败。

  • 面试官:如何防止重复提交订单?_第3张图片

  • 方案三

  • 优点:彻底解决了重复下单的问题,且技术方案做了一定简化;

  • 缺点:前后端仍然都有开发工作量,且需要新增字段;

方案四:从订单业务的本质入手

  • 先跟大家探讨一个概念,什么是订单?

  • 其实,订单就是某个用户用特定的价格购买了某种商品,即:用户和商品的连接。

  • 那么,“如何防止重复提交订单”,其实就是防止在短时间内,用户和商品进行多次连接。弄明白问题本质,接下来我们就着手制定技术方案了。

  • 可以用 ”用户ID + 分隔符 + 商品ID“ 作为唯一标识,让持有相同标识的请求在短时间内不能重复下单,不就可以了吗?而且,Redis不正是做这种解决方案的利器吗?

  • Redis命令如下:

  • SET key value NX EX seconds
    
  • 面试官:如何防止重复提交订单?_第4张图片

  • 把”用户ID + 分隔符 + 商品ID“作为Redis key,并把”短时间所对应的秒数“设置为seconds,让它过期自动删除。

  • 这样一来,整体业务步骤如下:在提交订单时,我们可以把”用户ID + 分隔符 + 商品ID“作为Redis key,并设置过期时间,让它可以到期自动删除。

  • 若Redis命令执行成功,则可以继续走下单的业务逻辑,执行不成功,直接返回给前端”下单失败“就可以了。

  • 面试官:如何防止重复提交订单?_第5张图片

  • 方案四

  • 从上图来看,是不是实现方式越来越简单了?

  • 优点:彻底解决了重复下单的问题,且在技术方案上,不需要前端参与,不需要添加接口,不需要添加字段;

  • 缺点:综合比较而言,暂无明显缺点,如果硬要找缺点的话,可能强依赖于Redis勉强可以算上吧;

你可能感兴趣的:(JAVA基础工作中实际总结,编程学习,mysql,jvm,java,服务器,算法)