缓存就是数据交换的缓冲区,缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码。
由于其被Static修饰,所以随着类的加载而被加载到内存之中,作为本地缓存,由于其又被final修饰,所以其引用和对象之间的关系是固定的,不能改变,因此不用担心赋值(=)导致缓存失效。
缓存数据存储于代码中,而代码运行在内存中,内存的读写性能远高于磁盘,缓存可以大大降低用户访问并发量带来的服务器读写压力
缓存穿透指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。先查Redis,再查数据库。当高度发的访问请求到达时,缓存穿透不仅增加了响应时间,而且还会引发对 DBMS 的高并发查询,这种高并发查询很可能会导致DBMS的崩溃。
①缓存空对象
优点:增强回写,实现简单,维护方便;缺点:增加内存消耗,数据不一致。
②布隆过滤器
优点:内存占用较少,没有多余key;缺点:实现复杂,存在误判可能。
当我们客户端访问不存在的数据时,先请求redis,但是此时redis中没有数据,此时会访问到数据库,但是数据库中也没有数据,这个数据穿透了缓存打到数据库,所以即使数据库不存在数据,也要存储到redis,下次访问不存在的数据也不会去请求数据库,而是请求redis。
布隆过滤器其实采用的是哈希思想来解决这个问题,通过一个庞大的二进制数组,走哈希思想去判断当前这个要查询的这个数据是否存在,如果布隆过滤器判断存在,则放行,这个请求会去访问redis,哪怕此时redis中的数据过期了,但是数据库中一定存在这个数据,在数据库中查询出来这个数据后,再将其放入到redis中,判断不存在,则直接返回。
总结一句话:将所有的key放到布隆过滤器+redis里面,其余返回null。
缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。
①redis中key设置永不过期或者过期时间不同、
②redis缓存集群实现高可用,即使用主从复制和哨兵机制以及开启redis持久化aof和rdb,恢复缓存集群。
③多缓存结合预防雪崩——ehcache本机缓存+redis缓存
④服务降级——哨兵限流和降级。
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。造成某一时刻的数据库请求量过大,压力剧增。
①差异失效时间,对于访问频繁的key,不设置过期时间。
②互斥锁
多个线程同时去查询数据库的这条数据,我们在第一个查询数据的请求上使用一个互斥锁来锁住
其他的线程走到这一步拿不到锁等着。等到第一个线程查询到数据做缓存,后面的线程进来发现已经有缓存,直接走缓存。
public Shop queryWithMutex(Long id) {
String key = CACHE_SHOP_KEY + id;
// 1、从redis中查询商铺缓存
String shopJson = stringRedisTemplate.opsForValue().get("key");
// 2、判断是否存在
if (StrUtil.isNotBlank(shopJson)) {
// 存在,直接返回
return JSONUtil.toBean(shopJson, Shop.class);
}
//判断命中的值是否是空值
if (shopJson != null) {
//返回一个错误信息
return null;
}
// 4.实现缓存重构
//4.1 获取互斥锁
String lockKey = "lock:shop:" + id;
Shop shop = null;
try {
boolean isLock = tryLock(lockKey);
// 4.2 判断否获取成功
if(!isLock){
//4.3 失败,则休眠重试
Thread.sleep(50);
return queryWithMutex(id);
}
//4.4 成功,根据id查询数据库
shop = getById(id);
// 5.不存在,返回错误
if(shop == null){
//将空值写入redis
stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
//返回错误信息
return null;
}
//6.写入redis
stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_NULL_TTL,TimeUnit.MINUTES);
}catch (Exception e){
throw new RuntimeException(e);
}
finally {
//7.释放互斥锁
unlock(lockKey);
}
return shop;
}