缓存穿透的原理和解决方案

使用设置空值的方式


以上属于被动防范,我们也可以主动采取措施,比如id复杂化,做好数据格式校验,热点参数限流
缓存击穿的原理和解决方案

1.互斥锁

线程1先发来请求,并且获取到了互斥锁。这时线程1就可以去查询数据库,接着将查询到的数据缓存到redis中,最后记得释放锁,不然以后别的线程无法访问数据库。如果在线程1获取互斥锁成功并且还未重建缓存、释放互斥锁的时候,线程2的请求到达,那么线程2无法在redis中获取到缓存,无法获取到互斥锁,那么我们就让线程休眠一会,比方休眠50毫秒,再让线程2去查询缓存,如果还是查询不到,那么再重新休眠,再重新查询。
解决方式:



2.逻辑过期

线程1查询缓存,逻辑时间过期,就获得锁,然后开启线程2去查询MYSQL,重建缓存,最后释放锁。在它释放锁之前呢,有别的线程过来查数据,获取锁失败,返回过期数据
解决方式:


public Result queryLuojiGuoqi(Long id) {
String s= RedisConstants.CACHE_SHOP_KEY + id;
String strJson = stringRedisTemplate.opsForValue().get(s);
if(StrUtil.isBlank(strJson)) {
return Result.fail("没有查到该店铺信息");
}
RedisData redisData = JSONUtil.toBean(strJson, RedisData.class);
Shop shop = JSONUtil.toBean((JSONObject) redisData.getObject(), Shop.class);
LocalDateTime localDateTime = redisData.getLocalDateTime();
if(localDateTime.isAfter(localDateTime.now())) {
return Result.ok(shop);
}
boolean lock = tryLock(RedisConstants.LOCK_SHOP_KEY + id);
if(lock) {
CACHE_REBUILD_EXECUTOR.submit(()->{
try {
showShop(id,20L);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
unlock(RedisConstants.LOCK_SHOP_KEY + id);
}
});
}
return Result.ok(shop);
}
总结:缓存穿透,防止别人高并发访问不存在的数据,数据库宕机(举例),我们设置空值来解决
缓存击穿,防止热点key失效期间,同时被高并发访问出事(举例),我们互斥锁了解决,或者添加逻辑过期解决。
有区别:互斥锁如果访问的数据不在缓存我们会查找数据库,写入缓存。而逻辑过期是我们提前把热点key存在缓存中,如果缓存没有,就一直找不到