缓存雪崩,缓存穿透,缓存击穿是什么?我们要怎么解决这些问题?

2022-11-18 15:23:52 浏览数 (1)

一 前言

在开发过程中,为了减轻数据库的压力,我们经常会使用到缓存,所以相信大家对缓存雪崩,缓存穿透,缓存击穿这三个名词并不陌生。今天就和大家一起分享一下,到底什么是缓存雪崩,缓存穿透,缓存击穿,我们要怎么做才能解决这些问题。

二:缓存雪崩

1.什么是缓存雪崩?

缓存雪崩是指在同一时间,大量缓存都失效,而恰巧这个时候,大量的请求同时进来,都去访问数据库,而数据库的连接数不够,从而导致用户端请求超时,响应慢,服务不可用等问题。

2.如何解决缓存雪崩问题?

①:在设置缓存的过期时间时,我们可以增加一个随机数。

代码语言:javascript复制
   /**
     * 如果不需要特别准确的过期时间的话 就加一个随机时间 防止缓存雪崩
     *
     * @param baseTime  原过期时间
     * @param isPrecise 是否需要准确的设置过期时间 true 是 false 否
     * @return
     */
    public static long getExpireTime(long baseTime, boolean isPrecise) {
        if (isPrecise) {
            return baseTime;
        }
        ThreadLocalRandom random = ThreadLocalRandom.current();
        return baseTime   random.nextInt(10 * 60);
    }

如果我们的缓存不依赖一个准确的过期时间,那么我们利用ThreadLocalRandom生成一个随机数,这样的话可以避免一部分缓存雪崩的情况。

注:如果你还对ThreadLocalRandom不了解的话,建议看我的另一篇文章:并发高的情况下,试试用ThreadLocalRandom来生成随机数,相信会对你有所帮助。

② 使用多级缓存

我们可以使用多级缓存,从而减少缓存雪崩的概率。例如我们可以用Caffeine作为本地缓存(一级缓存),redis作为二级缓存,一级缓存中找不到,再去二级缓存中找。这样的话可以大大减少缓存雪崩的概率。

③ 热key不设置过期时间

如果一个key查询特别频繁,但是修改的频率特别低,那么可以考虑不设置key的过期时间。这样也是可以解决缓存雪崩的问题,但是我并不推荐这么做,原因有以下几点:

1.缓存与数据库数据的一致性一直是一个无法彻底解决的问题,如果我们的缓存不设置过期时间,那么意味着一旦出现数据不一致的问题,影响的时候会特别长。

2.缓存并不是数据持久化的地方,我们不应该把缓存当做数据库来用。

④ 在高并发来之前,做一次缓存预热

如果我们知道什么时间段并发会特别高,那么我们可以提前将数据在缓存一遍,并且重新设置好过期时间,这样就可以避免在高并发的情况下,而我们的缓存刚好大面积失效的问题。

缓存雪崩这个问题,恰巧我们在生产环境中遇到过,因为我们是直播教学系统,大部分直播都是在晚上8点开始,所以导致8点左右的时候请求量会突然变多很多。有一次发现数据库突然报警,但是发现数据库连接特别多,但是我们都是有加redis缓存的,经过排查,发现是缓存过期了。虽然数据库顶住了,但是还是很慌。后续我们也是采用了方法①,②,④来处理的。

三:缓存击穿

1.什么是缓存击穿?

缓存击穿是指,在某一个时刻,一个热key突然失效了,这时候恰巧有很多请求需要访问这个key,导致大量的请求访问到数据库,从而导致数据库压力过大,最后导致服务不可用。

2.如何解决缓存击穿问题?

① 热点key不设置过期时间

热点key不设置过期时间,可以很大程度上避免缓存击穿问题,但是还是那句话,我并不推荐这么做,原因在前文已经叙述,这么就不再过多赘述。

② 使用多级缓存

多级缓存不仅可以很大程度上避免缓存雪崩,也可以很大程度上解决缓存击穿的问题,所以其实多级缓存还是很有必要的,利用caffeine做一个本地缓存,redis做二级缓存,这样的话还可以减少redis的压力。

③ 使用锁机制

我们可以使用分布式锁来限制只有一个线程去访问数据库,这样我们就不用担心大量请求打到数据库,从而导致数据库压力过大的问题了。如果我们使用Spring cache的@Cacheable注解来实现缓存,那么就更简单了,只需要设置注解的sync属性为true就可以了。

代码语言:javascript复制
@Cacheable(cacheNames= "cache",sync = true)
public User test(String name) {
  ...
  return user;
}

四:缓存穿透

1.什么是缓存穿透

缓存穿透是指用户端一直请求数据库中不存在的数据,而导致我们的缓存一直不生效,请求一直打到数据库,从而导致数据库压力过大,从而导致服务器不可用的问题。

2.缓存穿透的问题如何处理

① 针对业务,对一些参数进行校验

在查询缓存和数据库之前,可以根据我们的业务逻辑进行一些参数的校验,例如id 我们可以校验是否大于0,通常我们都会集成Validation注解做参数校验。这样可以避免一部分缓存穿透的问题。

② 如果数据库中查询结果为空,缓存一个空对象

如果数据库查询出来的结果是空的,如果是空集合,那么我们可以选择缓存一个空集合或者空对象到redis中,并设置一个较短的过期时间。但是如果遭到恶意的攻击,通常会导致我们缓存中都是一些无用缓存,而真正需要缓存的数据却被缓存淘汰策略所淘汰。

③ 建立黑名单,验证码等一系列安全机制,减少恶意请求

面对大量的恶意请求,我们应该建立一系列的安全措施,包括黑名单,验证码等等,这里就不做过多的举例了。

④ 使用布隆过滤器

使用布隆过滤器建立位图,通过布隆过滤器过滤掉大部分的不存在的数据。虽然布隆过滤器有一定的误判率,无法做到彻底拦截不存在的数据,但是也可以大大降低缓存穿透的概率了。

五 最后

今天和大家一起分析了缓存雪崩,缓存穿透,缓存击穿的问题,以及它们的一些解决方案。如果有什么疑问或者好的建议,欢迎在下方留言评论。

0 人点赞