码迷,mamicode.com
首页 > 其他好文 > 详细

一个因为不小心产生的分布式锁的线上bug

时间:2021-01-12 10:58:09      阅读:0      评论:0      收藏:0      [点我收藏+]

标签: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的上一层调用层。

一个因为不小心产生的分布式锁的线上bug

标签:redisson   接口   积分   pre   伪代码   应该   lazy   mit   for   

原文地址:https://www.cnblogs.com/gistmap/p/14256949.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!