缓存穿透
客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会到达数据库。会造成数据库宕机。
解决方案
1.缓存空对象
例如查询数据的id,发现数据库中没有,那么就在redis中缓存空对象。但是会有额外的内存消耗, 如果有很多数据都查不到,就会在redis中缓存很多null对象,垃圾数据。如果一开始数据在db中不存在,在redis中缓存了空对象。后来该数据有了,由于redis已经缓存了null对象,造成数据短期的不一致。
数据不一致可以采用两种办法,第一,给redis中缓存的key设置ttl过期时间,过一段时间数据将会一致。第二,当数据产生了插入db的后,也更新下缓存,将原来的缓存的null对象给覆盖掉。
2.布隆过滤
布隆过滤是一种算法,在客户端与redis之间有一个拦截布隆过滤器。
优点:内存占用少,没有多余的key。缺点:实现复杂,存在误判可能。
缓存空对象的代码实现
@Override
public Result queryById(Long id) {
String key = RedisConstants.CACHE_SHOP_KEY + id;
//从redis中查询商铺缓存
String shopJsonStr = stringRedisTemplate.opsForValue().get(key);
//redis中有数据直接返回
if(StrUtil.isNotBlank(shopJsonStr)) {
Shop shop = JSONUtil.toBean(shopJsonStr, Shop.class);
return Result.ok(shop);
}
//判断命中的是否为空值
if(shopJsonStr != null) {
//说明命中空字符串,不会去查数据库
return Result.fail("店铺不存在");
}
//redis中没有数据,继续查询数据库
Shop shop = getById(id);
if(ObjectUtil.isNull(shop)) {
//将空值写入redis
stringRedisTemplate.opsForValue().set(key, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
//数据库没有查询到数据,返回错误
return Result.fail("店铺不存在");
}
//数据库中查询到数据,存入redis,再返回数据;设置超时时间
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
return Result.ok(shop);
}
public static final Long CACHE_NULL_TTL = 2L;
主动的方式解决缓存穿透
1.增强id的复杂度,避免被猜测id的规律。
2.做好数据的基础格式校验。
3.加强用户权限校验,只有登录的用户有权限的用户才能访问。
4.做好热点参数的限流。
遗留问题:布隆过滤器的代码实现? 可以基于redisson实现。