防重复提交的方式汇总

Posted asfeixue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了防重复提交的方式汇总相关的知识,希望对你有一定的参考价值。

很多业务场景都需要防重复提交,比如提交订单,抢券,组团等场景。

在这里,主要陈述下一般的防重复提交方式。具体归类,会分为新增场景,更新场景。重复提交可以分为几种类型,比如:

短时间连续重复提交;

不定时间重复提交;

 

新增场景

新增场景无论是短时间连续重复提交还是不定时间重复提交,都是相似的解决方案。

  • 数据库加唯一索引

    作为常规手段,一般都会在数据库表中根据业务场景设计唯一索引。

执行步骤:

  1. 插入前检查;
  2. 执行insert ignore;
  3. 插入后判断更新行数等方式来避免重复提交的问题;

这种方式下,可以严格保证重复提交不会出问题。但是,性能要相对差一点。

  • 消息队列

    防重复提交的关键在于,并行变串行,消息队列也可以良好的承接这个需求。所有消息全部投入消息队列,然后逐一或者批量并行消费,在消费时因为可以进行校验与筛除,可以避免重复问题。此时不需要用唯一索引,因为请求直接投递到消息队列的缘故,接口的QPS可以非常高。对于仅需要保证投递成功,不关心处理结果的场景这个是非常适合的解决方案。

  • 分布式锁

    一般借助redis的带超时时间的NX锁。这种方式可以支持多个重复提交在超时时间内仅有一个提交会被处理。

执行步骤如下:

  1. 在获取锁之前检查是否已插入;
  2. 然后获取NX锁;
  3. 然后再检查一次是否插入;
  4. 然后再执行数据库写入即可;

检查2次,是为了规避ABA问题,防止提交事务成功,释放NX锁失败的情况导致重复的问题。这种方式超时时间要根据写数据库耗时来评估,留出一定倍数的超时时间空间,给db进行处理,然后查询动作需要从主库读,避免主从延迟带来读不到最新插入导致问题。这种方式有一定的隐患,比如db处理因为网络延迟,或者一个大事务在行锁等待,导致提交时间超过分布式锁的超时时间。这种情况可能造成重复提交的可能。建议配合spring事务的超时时间,合理设置事务超时,确保NX锁超时在事务超时后即可。这种方式可以及时返还请求方处理结果,对于需要有响应的场景比较适用。

  • 前端限制

    这种方式要看具体情况,有些业务场景可以仅允许用户提交一次;有些没有限制,允许用户提交多次。仅允许提交一次的情况下,前端按钮直接设置为不可点击即可。

更新场景

  • 数据库锁记录

    这种场景有悲观锁,乐观锁2种方式。

    悲观锁  

      可以在记录上加for update或者lock in share mode。lock in share mode允许共享读,加S锁,禁止写操作。for update不允许共享读,加X锁。在RC隔离级别下,这两者没有区别,因为RC级别读不加锁。不过为了性能,尽量不要使用这种级别的锁。

    乐观锁

      可以在表中引入版本列,通过version比较判断是否允许更新。

无论乐观锁还是悲观锁,都只能保证短时间并发操作的唯一性。不定时间的请求,需要在业务逻辑上进行逻辑保证。

以上是关于防重复提交的方式汇总的主要内容,如果未能解决你的问题,请参考以下文章

客户端防表单重复提交和服务器端防重复提交

接口防重复提交的技术解决方案

Spring Cloud项目如何防止重复提交,防重复提交幂等校验,Redis+aop+自定义Annotation实现接口

防重复提交利器--struts2令牌

使用Redis实现接口防重复提交

使用Redis实现接口防重复提交