标签:redisson 接口 积分 pre 伪代码 应该 lazy mit for
近日接手了一位离职同事的代码,跑了一个月并无并无任何异常,正开心时传来这个服务有个接口有问题。。
这是一个类似于提交问卷的接口,每个用户只可以提交一次并获得相应的积分。但那天出现网络波动用户领了N多积分。。下面是伪代码,同事用的是redisson的分布式锁。
@Transactional
public void commitForm(Long userId, Long activityId) {
lock.lock();
if (xxxService.isCommitted(userId, activityId)) {
// 抛异常
}
// 状态判断
// 条件判断
create(model);
// 是否有积分奖励
// 发放积分(dubbo接口调用)
lock.unlock();
}
那天的实际情况是用户提交了,界面转圈圈,用户狂点。结果是本应该同一个活动只能提交一次,数据库实际存了2条。
还有一个问题是,调用积分超时,我这边回滚了,但积分服务实际上最后成功了。
乍一看,真没啥问题啊,怎么会存了2条记录呢,不可能啊。。这,但看起来就是这个锁没起作用导致存了2条一样的。
我当时实在是发现不出有什么问题会导致这样,5分钟自己起了个Spring Boot + jpa + redisson 的服务去模拟,然后并发的去访问。发现还真是必存2~3条。
然后我突然就明了了。。马上一试,妥妥起效。
一张图就可以清晰说明为什么分布式锁看起来不起作用了。。之前一直没注意到这个@Transactional
在线程1出分布式锁时,但线程1的事务并未提交。所以线程2的isCommitted
去重判断是可以逃掉的。真相大白。
也就是说,只要保证分布式锁应在事务外层,即可保证这种情况不会发生。也就是把分布式锁挪到@Service的上一层调用层。
标签:redisson 接口 积分 pre 伪代码 应该 lazy mit for
原文地址:https://www.cnblogs.com/gistmap/p/14256949.html