缓存击穿------》缓存空对象""
缓存雪崩-----》添加缓存时设置过期时间并随机
缓存穿透------》互斥锁
@Override
public Result queryById(Long id) {
String key="cache:shop"+id;
//1.从redis中查询商铺缓存
String shopJson = stringRedisTemplate.opsForValue().get(key);
//2.判断是否存在
if(StrUtil.isNotBlank(shopJson)){
//3.存在
Shop shop=JSONUtil.toBean(shopJson,Shop.class);
return Result.ok(shop);
}
//判断是否是空值
/*一开始redis中根据key查,如果没有返回是一个null,如果这里让null就返回,那就到不了查数据库一部了,所以
* 缓存的空值是空字符串""*/
if (shopJson!=null){//是"";
return Result.fail("店铺信息不存在!");
}
//4.不存在根据id查询数据库---->先尝试获取锁
//4.1获取互斥锁
Shop shop= null;
String lockkey="lock:shop"+id;
try {
boolean islock = tryLock(lockkey);
if (!islock ) {//获取锁失败
//休眠重试
Thread.sleep(50);
queryById(id);//重新走此方法
}
//获取锁成功,去查数据库并写入缓存
shop = getById(id);
//5.数据库不存在,将空值写入缓存,避免缓存穿透。并返回错误信息
if (shop == null) {
//将空值写入redis
stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);
return Result.fail("店铺信息不存在!");
}
//6.存在,写入redis并返回
long randomLong = RandomUtil.randomLong(20L, 40L);//随机生成指定范围内的随机数
stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),randomLong, TimeUnit.MINUTES);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
unlock(lockkey);
}
return Result.ok(shop);
}
private boolean tryLock(String lockkey){//自定义一个尝试获取锁
//redis中有setnx key TTL 如果键不存在则新增,存在则不改变已经有的值。
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(lockkey, "1", 10, TimeUnit.SECONDS);
return BooleanUtil.isTrue(flag);
}
private void unlock(String lockkey) {//自定义释放锁
stringRedisTemplate.delete(lockkey);
}