Redis 中的缓存击穿、缓存穿透和缓存雪崩是什么?
Redis 中的缓存击穿、缓存穿透和缓存雪崩是什么?
回答重点
缓存击穿:指某个热点数据在缓存中失效,导致大量请求直接访问数据库。此时,由于瞬间的高并发,可能导致数据库崩溃。
缓存穿透:指查询一个不存在的数据,缓存中没有相应的记录,每次请求都会去数据库查询,造成数据库负担加重。
缓存雪崩:指多个缓存数据在同一时间过期,导致大量请求同时访问数据库,从而造成数据库瞬间负载激增。
解决方案
缓存击穿:
- 使用互斥锁,确保同一时间只有一个请求可以去数据库查询并更新缓存。
- 热点数据永不过期。
缓存穿透:
- 使用布隆过滤器,过滤掉不存在的请求,避免直接访问数据库。
- 对查询结果进行缓存,即使是不存在的数据,也可以缓存一个标识,以减少对数据库的请求。
缓存雪崩:
- 采用随机过期时间策略,避免多个数据同时过期。
- 使用双缓存策略,将数据同时存储在两层缓存中,减少数据库直接请求。
扩展知识
缓存雪崩解析
缓存雪崩是指在某个时间点,大量缓存同时失效或被清空,导致大量请求直接打到数据库或后端系统,造成系统负载激增,甚至引发系统崩溃。
这通常是由于缓存中的大量数据在同一时间失效引起的。
想象一个电商系统,用户量很大,假设某一系列的商品缓存突然同一时间失效,那就会造成我们的缓存雪崩,导致服务全部打到了数据库,引发严重后果。
解决办法
缓存键同时失效:
1)过期时间随机化:设置缓存的过期时间,加上一个随机值,避免同一时间大量缓存失效。
2)使用多级缓存:引入多级缓存机制,如本地缓存和分布式缓存相结合,减少单点故障风险。
3)缓存预热:系统启动时提前加载缓存数据,避免大量请求落到冷启动状态下的数据库。
4)加互斥锁:使得没缓存或缓存失效的情况下,同一时间只有一个请求来构建缓存,防止数据库压力过大。
缓存中间件故障:
1)服务熔断:暂停业务的返回数据,直接返回错误。
2)构建集群:构建多个 Redis 集群保证其高可用。
缓存击穿解析
缓存击穿指的是某一热点数据缓存失效,使得大量请求直接打到了数据库,增加数据库负载。
想象一下大家都在抢茅台,但在某一时刻茅台的缓存失效了,大家的请求打到了数据库中,这就是缓存击穿。
缓存雪崩是多个 key,大量数据同时过期,而缓存击穿是某个热点 key 崩溃,可以认为缓存击穿是缓存雪崩的子集。
解决办法
1)加互斥锁:保证同一时间只有一个请求来构建缓存,跟缓存雪崩相同。
2)热点数据永不过期:不要给热点数据设置过期时间,在后台异步更新缓存。
缓存穿透解析
缓存穿透是指查询一个不存在的数据,由于缓存中肯定不存在,导致每次请求都直接访问数据库,增加数据库负载。
攻击者可以通过构造不存在的 key 发起大量请求,对数据库造成很大的压力,可能会造成系统宕机。
解决办法
1)防止非法请求:检查非法请求,封禁其 IP 以及账号,防止它再次为非作歹。
2)缓存空值:将数据库中不存在的结果(例如空值)也缓存起来,并设置一个较短的过期时间,避免频繁查询数据库。
3)使用布隆过滤器:使用布隆过滤器来快速判断一个请求的数据是否存在,如果布隆过滤器判断数据不存在,则直接返回,避免查询数据库。
互斥锁实现示例(Java)
1 | public class CacheService { |
说明:
- 缓存查询:首先尝试从 Redis 中获取数据。
- 加锁:如果缓存中没有数据,使用
ReentrantLock加锁,确保只有一个线程可以查询数据库。 - 二次检查:在加锁后再次检查缓存,避免重复查询。
- 数据库查询:如果缓存仍然没有数据,查询数据库并将结果存入缓存。
- 释放锁:确保锁在查询结束后被释放,以防止死锁。
这种方式有效地防止了缓存击穿,因为即使在高并发的情况下,只有一个请求会去数据库查询数据,其他请求则会等待锁释放。如果后端是多实例部署,一般实例数量也不多,即使使用本地锁也行,因为并发也不高。