RedisTemplate报错WRONGTYPE Operation against a key holding the wrong kind of value

场景

今天写业务代码,先从redis中读取值,不存在的话加redis锁,锁定之后再次尝试读取一次redis,没有值的话去db中读取并写redis缓存。
非常简单的代码,确报错WRONGTYPE Operation against a key holding the wrong kind of value

分析

错误发生在锁定之后再次读取redis的时候,报错的字面意思很简单,key值类型不对,但是理论上这个时候不是还没有这个缓存key吗,就算有,也是其他线程或者用户实例给设置的缓存,怎么可能报错key类型不对。照着源码排查了我十几分钟无果,直到我看了下我的业务代码:

    @Override
    public List<ImagePoolDto> listImagePoolAndImage(VrImagePoolListQueryVo vo) throws Exception {
        // 从缓存中获取
        String cacheKey = VrImagePool.cacheKey(vo.getParentId(), vo.getTitle());
        List<ImagePoolDto> cache = this.redisService.getListFromRedis(cacheKey, ImagePoolDto.class);
        if (null != cache) {
            return cache;
        } else {
            // 缓存中没有,从db中获取并写缓存
            AtomicBoolean cacheFlag = new AtomicBoolean(false);
            AtomicReference<List<ImagePoolDto>> cacheRef = new AtomicReference<>();
            AtomicReference<List<ImagePoolDto>> dbRef = new AtomicReference<>();
            RedissonUtils.lockRun(this.redissonClient.getLock(cacheKey), () -> {
                // 重新读缓存,防止并发写缓存
                List<ImagePoolDto> cacheReadRepeat = this.redisService.getListFromRedis(cacheKey, ImagePoolDto.class);
                if (null != cacheReadRepeat) {
                    cacheRef.set(cacheReadRepeat);
                    cacheFlag.set(true);
                    return;
                }
                List<ImagePoolDto> db = this.baseMapper.selectListImagePoolAndImage(vo);
                this.redisService.setObjectRandomExpireTime(cacheKey, db, 60, 120);
                dbRef.set(db);
            });
            if (cacheFlag.get()) {
                return cacheRef.get();
            } else {
                return dbRef.get();
            }
        }
    }

我真是个傻逼,获取锁的时候直接用的缓存key,这就导致进入锁之后,redis中事实上存在了一个键,只不过查询不到,导致读取的时候报错类型不对。当时写代码的时候太着急了,没有注意到。
代码改成如下,正常了:

    public List<ImagePoolDto> listImagePoolAndImage(VrImagePoolListQueryVo vo) throws Exception {
        // 从缓存中获取
        String cacheKey = VrImagePool.cacheKey(vo.getParentId(), vo.getTitle());
        List<ImagePoolDto> cache = this.redisService.getListFromRedis(cacheKey, ImagePoolDto.class);
        if (null != cache) {
            return cache;
        } else {
            // 缓存中没有,从db中获取并写缓存
            AtomicBoolean cacheFlag = new AtomicBoolean(false);
            AtomicReference<List<ImagePoolDto>> cacheRef = new AtomicReference<>();
            AtomicReference<List<ImagePoolDto>> dbRef = new AtomicReference<>();
            RedissonUtils.lockRun(this.redissonClient.getLock(cacheKey + "#lock"), () -> {
                // 重新读缓存,防止并发写缓存
                List<ImagePoolDto> cacheReadRepeat = this.redisService.getListFromRedis(cacheKey, ImagePoolDto.class);
                if (null != cacheReadRepeat) {
                    cacheRef.set(cacheReadRepeat);
                    cacheFlag.set(true);
                    return;
                }
                List<ImagePoolDto> db = this.baseMapper.selectListImagePoolAndImage(vo);
                this.redisService.setObjectRandomExpireTime(cacheKey, db, 60, 120);
                dbRef.set(db);
            });
            if (cacheFlag.get()) {
                return cacheRef.get();
            } else {
                return dbRef.get();
            }
        }
    }

版权声明:本文为SingingFisher原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。