Redis缓存穿透,雪崩,击穿

一、缓存

1、定义

缓存就是数据交换的缓冲区,缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码。

由于其被Static修饰,所以随着类的加载而被加载到内存之中,作为本地缓存,由于其又被final修饰,所以其引用和对象之间的关系是固定的,不能改变,因此不用担心赋值(=)导致缓存失效。

2、作用

缓存数据存储于代码中,而代码运行在内存中,内存的读写性能远高于磁盘,缓存可以大大降低用户访问并发量带来的服务器读写压力

二、缓存穿透

1、产生原因

缓存穿透指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。先查Redis,再查数据库。当高度发的访问请求到达时,缓存穿透不仅增加了响应时间,而且还会引发对 DBMS 的高并发查询,这种高并发查询很可能会导致DBMS的崩溃。

2、解决方案

①缓存空对象

优点:增强回写,实现简单,维护方便;缺点:增加内存消耗,数据不一致。

②布隆过滤器

优点:内存占用较少,没有多余key;缺点:实现复杂,存在误判可能。

3、缓存空对象思路

当我们客户端访问不存在的数据时,先请求redis,但是此时redis中没有数据,此时会访问到数据库,但是数据库中也没有数据,这个数据穿透了缓存打到数据库,所以即使数据库不存在数据,也要存储到redis,下次访问不存在的数据也不会去请求数据库,而是请求redis。

4、布隆过滤器思路

布隆过滤器其实采用的是哈希思想来解决这个问题,通过一个庞大的二进制数组,走哈希思想去判断当前这个要查询的这个数据是否存在,如果布隆过滤器判断存在,则放行,这个请求会去访问redis,哪怕此时redis中的数据过期了,但是数据库中一定存在这个数据,在数据库中查询出来这个数据后,再将其放入到redis中,判断不存在,则直接返回。

总结一句话:将所有的key放到布隆过滤器+redis里面,其余返回null。

三、缓存雪崩

1、产生原因

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

2、解决

①redis中key设置永不过期或者过期时间不同、

②redis缓存集群实现高可用,即使用主从复制和哨兵机制以及开启redis持久化aof和rdb,恢复缓存集群。

③多缓存结合预防雪崩——ehcache本机缓存+redis缓存

④服务降级——哨兵限流和降级。

四、缓存击穿

1、产生原因

缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。造成某一时刻的数据库请求量过大,压力剧增。

2、解决

①差异失效时间,对于访问频繁的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;
    }

你可能感兴趣的:(redis,redis,缓存,数据库)