缓存穿透与雪崩(面试高频)

Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据査询方面。但同时,它也带来了一些问题。其中,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存。另外的一些典型问题就是,**缓存穿透、缓存雪崩和缓存击穿。**目前,业界也都有比较流行的解决方案。

缓存穿透

image

缓存穿透概念

缓存穿透的概念很简单,用户想要査询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库(mysql)查询。发现也没有,于是本次查询失败。**当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。**这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。

解决方案

  • 布隆过滤器(推荐)

    • 在数据写入数据库的同时将这个 ID 同步到到布隆过滤器中,当请求的 id 不存在布隆过滤器中则说明该请求查询的数据一定没有在数据库中保存,就不要去数据库查询了。

    布隆过滤器原理:

    image

  • 缓存空值

    • 当请求的数据不存在 Redis 也不存在数据库的时候,设置一个缺省值(比如:None)。当后续再次进行查询则直接返回空值或者缺省值。

缓存击穿

image

缓存击穿 概念

这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库就像在一个屏障上凿开了一个洞。
当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来査询最新数据,并且回写缓存,会导使数据库瞬间压力过大。

缓存击穿 和 缓存穿透的区别是:

  • 缓存穿透
    • 查不到数据,没有命中缓存,直接绕过缓存查询数据库
  • 缓存击穿
    • 查的到,但是缓存过期的一瞬间大量请求直接请求数据库,高并发。

解决方案

  • 设置热点数据永不过期

    • 但是这样会出现很多冗余 因为永不过期 那么它就不会丢失删除
  • 加互斥锁(分布式锁)

    • 使用分布式锁,保证对于每个key同时只有一个线程去查询后端,其他线程没有分布式锁的权限,因此只需要等待。这种就将高并发的压力转移至分布式锁中。

    伪代码:

    public Object getData(String id) {
        String desc = redis.get(id);
            // 缓存为空,过期了
            if (desc == null) {
                // 互斥锁,只有一个请求可以成功
                if (redis(lockName)) {
                    try {
                        // 从数据库取出数据
                        desc = getFromDB(id);
                        // 写到 Redis
                        redis.set(id, desc, 60 * 60 * 24);
                    } catch (Exception ex) {
                        LogHelper.error(ex);
                    } finally {
                        // 确保最后删除,释放锁
                        redis.del(lockName);
                        return desc;
                    }
                } else {
                    // 否则睡眠200ms,接着获取锁
                    Thread.sleep(200);
                    return getData(id);
                }
            }
    }
    

缓存雪崩

image

缓存雪崩 概念

缓存雪崩指的是大量的请求无法在 Redis 缓存系统中处理,请求全部打到数据库,导致数据库压力激增,甚至宕机。

出现该原因主要有两种:

  • 大量热点数据同时过期,导致大量请求需要查询数据库并写到缓存;
  • Redis 故障宕机,缓存系统异常。

缓存大量数据同时过期

数据保存在缓存系统并设置了过期时间,但是由于在同时一刻,大量数据同时过期。

系统就把请求全部打到数据库获取数据,并发量大的话就会导致数据库压力激增。

缓存雪崩是发生在大量数据同时失效的场景,而缓存击穿(失效)是在某个热点数据失效的场景,这是他们最大的区别。

解决方法

  • 过期时间添加随机值(数据预热)

    • 要避免给大量的数据设置一样的过期时间,过期时间 = baes 时间+ 随机时间(较小的随机数,比如随机增加 1~5 分钟)。

    • 这样一来,就不会导致同一时刻热点数据全部失效,同时过期时间差别也不会太大,既保证了相近时间失效,又能满足业务需求。

  • 限流降级

    • 这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查,询数据和写缓存,其他线程等待
文章作者: 面具
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 MianJu —— 这只是一个 Title 而已~
redis 面试 redis
喜欢就支持一下吧