美文网首页
redis事务的坑,opsForValue().incremen

redis事务的坑,opsForValue().incremen

作者: 尹楷楷 | 来源:发表于2022-08-22 15:04 被阅读0次

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

    场景说明

    这里的方法放回null

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

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

        public <T> List<T> rightPop(String key, Class<T> 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<T> list = (List<T>) 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 <a href="https://redis.io/commands/incrby">Redis Documentation: INCRBY</a>
         */
        @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().incremen

          本文链接:https://www.haomeiwen.com/subject/oasigrtx.html