看到这个标题,你肯定在想:什么是雪崩?什么是穿透?什么是击穿?怎么感觉都是一个意思,都是缓存失效导致的数据库压力问题。
这三个问题的确都会导致数据库压力问题,但是出现的场景还是存在区别滴。在处理手段上也有很大的区别。
缓存雪崩
需要使用的热点数据都做了缓存,但是为了保证Redis的内存够用,所以会对一些数据进行设置过期时间操作。一般情况下存在定时任务去刷新缓存信息,这个时候就存在一个隐患的问题:假设在定时任务中设置的过期时间都一样,那么在某一个时间点时,大量的key同时过期。本来缓存抵挡住了大量的请求,key过期后,这些压力全部同时打到了数据库中,数据库可能就扛不住。直接被打挂了,重启数据库后,立马又被新的流量打挂了。这就是缓存雪崩。
(注:这里还有个隐患,Redis可能会频繁的处理过期key,从而导致Redis性能降低)
处理缓存雪崩主要有以下几个思路:
- 分散过期时间。把每个key的过期时间都加上一个随机的过期值。保证数据不会在同一时间大面积失效。
- 设置过期标志更新缓存。给每一个缓存数据都增加一个相应的缓存标记,记录缓存是否失效。如果缓存失效,则更新数据缓存。这样虽然在缓存首次失效时,仍会带来数据库压力问题。但是能够在一定程度上缓解后续被新流量打挂以及所有服务不可用的情况。
- 在代码层面上设定一定策略,比如加锁等待、减慢请求速度。给数据库留下启动和重建缓存的机会。
缓存穿透
缓存穿透就是指客户端不断的查询缓存和数据库都没有的数据。相当于进行了两次无用的查询。降低缓存的命中率,增加数据库的压力。
解决手段:
- 客户端进行校验,先将明显不符合的key给过滤掉。
- 对于查询不到的数据,直接赋值一个null给它。
- 使用布隆过滤器来对不存在数据进行过滤掉。
- 限流。
缓存击穿
缓存击穿和缓存雪崩有点像。但是又不一样,缓存雪崩是大面积的缓存同时失效,打崩了DB。而缓存击穿是指一个key非常热点,在不停的扛着大并发,当这个key失效的瞬间,持续的大并发就击穿缓存,直接请求数据库。
解决手段:
主要场景是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它。
其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存。