1.瞎聊:
想必大家对于幂等性这概念应该不陌生吧,在日常开发中很多操作都需要做到一个幂等操作,比如:新增一个用户,很多时候网络卡,或者系统响应慢,用户快速点击了2次新增用户,后台服务由于数据库事务的隔离级别(mysql的可重复读)或者线程安全而导致重复插入,我画一张图大家意思意思。
1.1 情况1
如果是这种情况将由于线程安全问题而导致重复插入(其实还不一定):
1.2 情况2
这种情况由于数据库的隔离级别(可重复读)而导致重复插入数据:
注意:上面的步骤作为一个参考即可,其实有很多情况组合都可以重现上诉几种情况。 情况2出现幂等问题是由于2个事务都同时存在,不管如何操作,即使是情况1也会出现重复插入的情况,只要做同一个操作并且2个事务同时存在就可能会出现这个问题(文字不好描述)。
2.解决思路
思路1:
加 Synchronized 锁或者ReentrantLock 等等都可以,但是需要注意,加锁时机也需要注意,如果不能解决事务隔离问题(可重复读)即使加锁也没用
思路2:
如果是修改数据则可以 在表字段后面加一个版本号字段,本质上也是一种乐观锁的体现(这种新增数据的情况是无法用版本号的),为什么加一个版本号字段就可以?
我们在修改某条数据的的时候先查询这条数据的版本号 A,然后 update的是附带where 数据库那条数据最新版本=A,不用担心是否在存在2个事务的情况发生重复读的问题,因为update会直接读取得最新的版本链数据,即使存在多个事务,且该事务没有提交,update也依旧会读取最新的数据。我们只要判断sql返回的影响数就可以判断 有没有update成功,没有update的成功说明更新失败,到底是重写撤销还是重试还是有我们自己进行定义。
思路3:
我们可以借鉴解决表单重复提,在跳转新增的页面向后台请求一个key,在新增用户将key带到后台,后台在处理请求的时候先校验key是否存在,如果存在,则将将key删除,并真正处理请求(但是要注意这里也会存在线程安全问题)
思路4:
这种感觉不可靠,但是做小项目,直接在前段把幂等做好,比如点击新增后按钮回调,或者刷新页面,或者必须延迟几秒后才能点击等等。