生产问题一:redis锁处理幂等性失效

发布时间:2023年12月31日
伪代码:?
    @Transactional(rollbackFor = Exception.class)
    public void add(User user) {
        log.info("add user params user:{}", JSON.toJSONString(user));
        Assert.isTrue(StringUtils.isNotBlank(user.getIdCard()), "身份证号不允许null");
        String key = "key";
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        try {
            long count = userMapper.selectCount(user);
            if (count == 0) {
                userMapper.insert(user);
            }
        } catch (Exception e) {
            log.error("add user error", e);
        } finally {
            lock.unlock();
        }
        System.out.println("并发执行,同时插入了两条");
    }

问题分析:

如果有两个线程a,b。如果a线程释放锁后,退出方法前,让出时间片,由于方法未执行完,此时事务没有提交,那么b线程在去数据库查询的时候仍然出来count为0,执行了insert操作,两个线程执行完该方法提交事务,此时数据库中会增加两条数据,幂等性失效。

解决方式:

第一种:

    //@Transactional(rollbackFor = Exception.class)
    public void add(User user) {
        log.info("add user params user:{}", JSON.toJSONString(user));
        Assert.isTrue(StringUtils.isNotBlank(user.getIdCard()), "身份证号不允许null");
        String key = "key";
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        try {
            long count = userMapper.selectCount(user);
            if (count == 0) {
                userMapper.insert(user);
            }
        } catch (Exception e) {
            log.error("add user error", e);
        } finally {
            lock.unlock();
        }
        System.out.println("并发执行,同时插入了两条");
    }

去掉@Transactional注解,这样在a线程在insert时候就会自动提交事务,a释放锁后,b在查询时候,count不等0,不执行插入操作。

方法二:

public class Test {

    @Resource
    private UserMapper userMapper;

    @Resource
    private UserService userService;

    @Resource
    private RedissonClient redissonClient;

    public void test(User user) {
        String key = "key";
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        try {
            userService.addOk(user);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void addOk(User user) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .eq(User::getIdCard, user.getIdCard());
        long count = userMapper.selectCount(wrapper);
        if (count == 0) {
            userMapper.insert(user);
        }
    }
}

将锁放到到事务提交后

文章来源:https://blog.csdn.net/f746262041/article/details/131354539
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。