基于redis缓存查询店铺
@Override
public Result queryById(Long id) {
//从redis中查询商铺缓存
String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);
//判断缓存是否命中
if (StrUtil.isNotBlank(shopJson)){
//命中,则返回店铺信息
Shop shop = JSONUtil.toBean(shopJson, Shop.class);
return Result.ok(shop);
}
//未命中,根据id查询数据库
Shop shop = getById(id);
//不存在该店铺,报错
if (shop==null){
return Result.fail("店铺不存在!");
}
//存在,写入redis
stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);
//返回
return Result.ok(shop);
}
缓存更新策略
3.1 先删缓存,再操作数据库
如下图:在多线程的情况下,可能出现的问题。
如果线程1要更新数据库:先删了缓存,这是线程2想要读取数据,首先会查询缓存是否命中,这时就不能命中,然后就会去查询数据库,然后再写缓存,线程2执行完了线程1才去更新数据库,这时候就会导致缓存中的数据是旧数据。
可能性:明显更新数据库的操作更加耗费时间,所以如果需要更新数据库的时候是先删缓存再更新的话,发生缓存和数据库数据不一致的可能性还是很高的。
3.2 先更新数据库再删缓存
如下图:线程一先读取数据,并且此时缓存没有命中(可能是缓存已经超过了失效时间了),然后线程2来了,执行了更新数据库和删缓存,然后线程一才写缓存(写入的是线程1开始从数据库中查到的旧数据),这时也会导致数据的不一致。
可能性:线程1中读取和写缓存的耗费时间没有线程二多,更新操作明显更耗时,所以这种要发生的可能性不大。
所以先更新数据库再删缓存是更好的选择。
缓存失效了怎么办?
缓存穿透
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库(一些别有用心的人就会利用这一点不断的发出这样的请求,导致服务器压力变大)。
常见的两种解决方案:
缓存空对象
*优点:实现简单,维护方便
*缺点:1.额外的内存消耗. 2.可能存在数据短期的不一致。
布隆过滤
优点:内存占用较少。没有多余的key。
缺点:1.实现复杂 2.存在误判的可能。
布隆过滤器可以简单的理解为:是一个bite数组,里面存的是二进制位,不是把数据库里面的数据存储到布隆过滤器里面,而是把这些数据基于某种Hash算法计算出Hash值,然后再将这些Hash值转换成二进位保存到布隆过滤器里面,当我们要去判断数据库里面的数据是否存在的时候,其实就是判断对应的位置是0还是1.
基于redis缓存查询店铺考虑缓存穿透问题
@Override
public Result queryById(Long id) {
//从redis中查询商铺缓存
String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);
//判断缓存是否命中
if (StrUtil.isNotBlank(shopJson)){
//命中,则返回店铺信息
Shop shop = JSONUtil.toBean(shopJson, Shop.class);
return Result.ok(shop);
}
//命中为"",说明在数据库中店铺不存在
if (shopJson !=null){
return Result.fail("店铺不存在!");
}
//未命中,根据id查询数据库
Shop shop = getById(id);
//不存在该店铺,报错
if (shop==null){
//将空值写入redis中
stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY +id,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
return Result.fail("店铺不存在!");
}
//存在,写入redis
stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);
//返回
return Result.ok(shop);
}
缓存雪崩
缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大的压力。
解决方案
1.由于大量的缓存同时失效,造成原因可能就是TTL时间到了。所以:给不同的Key的TTL添加随机值。
2.针对redis宕机:搭建redis集群形成主从,假设主宕机了,我们就可以从 从机替代,这样就可以确保redis可以一直对外提供服务。
3.给缓存业务添加降级限流策略
4.给业务添加多级缓存
缓存击穿
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
假设:一个热点key失效了,当线程1来了,发现没有缓存,就会去重建缓存,这个过程又比较久,在重建的过程中仍然有许多的请求。
常见的两种解决方案:
1.互斥锁
缺点:性能比较差。因为在构建缓存的过程中,其他的线程都要等待。
2.逻辑过期
也就是在缓存中多了一个逻辑过期时间。而没有真的去设置TTL过期时间。