场景
今天写业务代码,先从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版权协议,转载请附上原文出处链接和本声明。