Redis缓存穿透及在使用存null对象解决的案例
什么是redis的缓存穿透:
查询一个不存在的数据,数据库查询不到数据,也不会存入缓存,每次请求都会去查询数据库。
如果是坏人使用很多的线程并发向这个数据发送请求,可能会导致数据库崩溃
解决办法
1.使用布隆过滤器来解决
什么是布隆过滤器
用于检索一个元素是否存在于一个集合中。底层是初始化一个比较大的数组,存放二进制0或者1,当一个key经过三次hash计算后,找到对应数组的下标并将0改为1,三个数组的位置就能找到一个key。但是会产生一定的误判。
在客户端和redis之间添加一个布隆过滤器,客户端请求过来,布隆过滤器去查询这个数据是否存在,不存在就直接拒绝请求,存在则去继续查询redis。
2.存储一个null对象
请求不存在的数据,会缓存null到redis中。
优点:实现比较简单,维护方便
缺点:缓存大量的null会导致redis产生额外的内存消耗。(可以给这些null设置一个比较短的TTL)
案例:黑马点评查询店铺缓存
代码实现:
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public Result queryById(Long id) {
//缓存穿透 使用存null解决
Shop shop = queryWithPassThrough(id);
if (shop == null) {
return Result.fail("店铺不存在!");
}
return Result.ok(shop);
}
//封装缓存穿透(查询不存在的数据将其以null的形式缓存到redis中)的方法
public Shop queryWithPassThrough(Long id) {
String key = CACHE_SHOP_KEY + id; //缓存的key
//1.从redis中查询商铺缓存
String shopJson = stringRedisTemplate.opsForValue().get(key);
//2.判断缓存是否存在 只有shopJson是非空字符串才为true
if (StrUtil.isNotBlank(shopJson)) {
//3.存在,直接返回
return JSONUtil.toBean(shopJson, Shop.class);
}
//由于使用缓存null对象解决需要判断是否是""
if ("".equals(shopJson)) {
return null;
}
//4.不存在,根据id查询数据库
Shop shop = getById(id);
//5.数据库不存在,返回错误 (解决缓存穿透)存一个null数据
if (shop == null) {
//(解决缓存穿透)存一个null数据 并且设置过期时间防止一直占用内存
stringRedisTemplate.opsForValue().set(key, "", 2L, TimeUnit.MINUTES);
return null;
}
//6.存在,写入redis 设置超时时间
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop),30L, TimeUnit.MINUTES);
//7.返回
return shop;
}
上一篇: 几秒钟就充满电!科学
下一篇: 暂无数据