redis事务的坑,opsForValue().increment 返回null

线上运行一段时间后就NPE,本地又重现不了。一开始以为是框架的bug,后面发现还是人为导致的...

场景说明

这里的方法放回null

    public Long increment(String key, long i) {
        return redisTemplate.opsForValue().increment(key,i);
    }

这是另一个接口,将全局的事务支持设置为了true。非常怀疑是这段代码导致的。因为项目启动后一段时间还运行良好,过一段时间就NPE了。

    public  List rightPop(String key, Class clazz, int num){
        Long size = redisTemplate.opsForList().size(key);
        if(num > size){
            return null;
        }

        redisTemplate.watch(key);
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.multi();
        try {
            redisTemplate.opsForList().range(key, 0, num-1);
            redisTemplate.opsForList().trim(key, num, -1);
            List exec = redisTemplate.exec();
            if(null != exec){
                List list = (List) exec.get(0);
                return list;
            }
        } catch (Exception e) {
            e.printStackTrace();
            redisTemplate.discard();
        }finally {
            redisTemplate.unwatch();
        }
        return null;
    }

怀疑的问题代码: redisTemplate.setEnableTransactionSupport(true);

查看源码

在事务环境下会返回null

@return {@literal null} when used in pipeline / transaction.

    /**
     * Increment an integer value stored as string value under {@code key} by {@code delta}.
     *
     * @param key must not be {@literal null}.
     * @param delta
     * @return {@literal null} when used in pipeline / transaction.
     * @see Redis Documentation: INCRBY
     */
    @Nullable
    Long increment(K key, long delta);

但是引发空指针的这段代码并没有显式地添加redis事务啊。难不成业务方法的 @Transactional传播过来了?

重现

test1方法

    @GetMapping("/test1")
    public void test1() throws IOException {
        redisTemplate.watch("1");
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.multi();
        try {
            redisTemplate.opsForList().range("1", 0, 1-1);
            redisTemplate.opsForList().trim("1", 1, -1);
            List exec = redisTemplate.exec();
        } catch (Exception e) {
        }finally {
            redisTemplate.unwatch();
        }
    }

test2方法,必须加 @Transactional(rollbackFor = Exception.class)注解!!

    @GetMapping("/test2")
    public void test2() throws IOException {
        productService.test();

    }

   @Transactional(rollbackFor = Exception.class)
    @Override
    public void test() {
        Long increment = redisTemplate.opsForValue().increment("22", 1);
        System.out.println(increment);
    }

修正

在finally中关闭事务

finally {
            redisTemplate.setEnableTransactionSupport(false);
            redisTemplate.unwatch();
}

结论

1、 @Transactional、redisTemplate.setEnableTransactionSupport(true); 同时被设置,那么将以事务的方式执行,这时候 opsForValue().increment 返回null
2、redis事务不要瞎用,有坑的

你可能感兴趣的:(redis事务的坑,opsForValue().increment 返回null)