美文网首页面试
如何防止订单重复提交以及如何防止订单重复确认

如何防止订单重复提交以及如何防止订单重复确认

作者: SteveGuRen | 来源:发表于2017-04-27 17:04 被阅读5951次

    订单重复问题已经是老生常谈的问题了,我们熟悉的淘宝购物流程,购物车-->生成订单--》提交订单--》订单确认--》支付订单--》备货--》发货,这里可能会有一些问题,例如,有人恶意或者无意的重复提交订单,从而导致数据库保存的订单数量与实际不符,重复提交订单导致用户体验不佳,甚至更为严重的是,用户重复支付了同一个订单。所以,涉及到订单,应该首先想清楚如何设计才能保证系统不会出现这些问题

    如何防止订单重复提交

    首先说两个我们购物时经常有过的体验或者说购物网站的网页提醒

    • 你提交的动作过快,请稍后尝试
    • 你的订单已经超时,请刷新页面后重新提交
      看到这些提示,说明该购物网站做了订单提交的限制,一方面是防止有人恶意无限制提交订单,所以限制了一定时间内最大可操作次数,另一方面是为了保证订单无重复提交。那么这是怎么做到的呢?
      第一个应该比较简单,限制某个时间内的最大操作次数只需要有一个计数器就可以,计数器可以用redis实现,设置一个带有有效时间的值作为计数器,如果值不存在则自动创建,超过某一个值就认为操作次数用完即可以实现。
      第二个可以使用token机制,token即令牌,学过spring security的相信对这个词不会陌生。我们可以使用类似spring security的机制在页面上生成一个token,当提交订单时,根据该token的有效时间和允许的使用次数来判断订单是否允许提交,从而规避重复提交的问题。当然,有人会问,在高并发的情况下,如果是判断token有效之前有很多同一个用户的提交线程过来(用户正常使用一般不会出现这种情况,一般是压力测试工具导致的),那么还是会重复提交,所以,这里需要用到锁机制,访问同一个用户的token同一时间只能有一个线程,token使用之后失效了就会被清掉,之后的线程就找不到该token,从而认为订单不能提交。

    订单确认支付

    如支付宝和微信等,支付宝和微信本身是怎么限制订单只能支付一次的呢?订单怎么保证只会被支付一次呢?这个问题相对来说就简单很多了,同一订单的状态更新的SQL只需要带上条件,利用的是数据库的行锁。当然,如果是分布式系统,这里涉及到的问题会更多。

    update table item set item.status=:newstatus where item.id = :id and item.status = oldstatus.
    

    对比案例

    • 美团GTIS
      主要看GTIS的流程图以及想想其交易ID的作用,交易之前,后台会返回一个交易ID给前端,前端在点击交易按钮时需将该交易ID和其他交易信息同时返回给后台进行处理,通过全局的交易ID实现“该次交易的”幂等性
      美团GTIS.png

    参考资料和研究

    • 分布式锁的对比分析:非常好的一篇文章,包含下面的关键词:可重入、阻塞、公平、排他、乐观、悲观、单点、死锁发生、连接池状况、实现种类、实现方式

    相关文章

      网友评论

      • 吴摩西_f3ac:在你上面第二种方案中,说到加锁,来限制访问token,请教一下,怎么设计这个锁,才能降低对系统性能的影响呢?(这是目前我见到最美的blog,):joy:
        吴摩西_f3ac:@邓慧智 我们把token放在redis中,:joy:
        SteveGuRen:这个只是启发性博文,写的不够专业,欢迎讨论:grin:
        SteveGuRen:保证token是线性安全的方法有很多,锁粒度越小,并发度越高,假设你token保存在主库的某张表的某条记录,你可以通过数据库的行锁实现;假设你的token保存在redis之类的,那么你也可以用redis相关的原子操作去操作这个token。你的问题我也不知道怎么答,token,用户id,订单id都会影响锁粒度吧。

      本文标题:如何防止订单重复提交以及如何防止订单重复确认

      本文链接:https://www.haomeiwen.com/subject/tjnizttx.html