redis的缓存穿透、缓存击穿和缓存雪崩
Redis 面试中经常会被问到关于缓存穿透、缓存击穿和缓存雪崩的问题。这三个概念描述了在使用 Redis 作为缓存服务时可能出现的不同异常情况,以及相应的解决方案。下面分别对这三个现象进行解释,并给出应对策略:
缓存穿透
现象:
缓存穿透是指查询请求对应的缓存和数据库中均不存在数据。正常情况下,如果数据不存在于缓存中,系统会尝试从数据库加载数据并放入缓存以备后续使用。但如果频繁或恶意地请求那些数据库中也不存在的数据(例如,非法或不存在的 ID),就会导致大量的请求直接穿透缓存到达数据库,增加了数据库的压力,甚至可能导致数据库崩溃。
解决方案:
- 请求校验:对请求进行合法性检查,如对查询条件进行范围限制、格式校验等,过滤掉明显不可能存在的数据请求。
- 布隆过滤器(Bloom Filter):在查询缓存前,先通过布隆过滤器判断请求的 key 是否可能存在。布隆过滤器是一种空间效率极高的概率型数据结构,它能以较低的误判率快速告诉客户端某个 key 极大概率不存在,从而避免无效的数据库查询。
- 空值缓存:对于数据库返回为空的查询结果,仍然在缓存中设置一个特殊值(如标记为"not found"或过期时间很短的键值对),这样后续相同的无效请求就可以直接在缓存层面得到响应,避免穿透到数据库。
缓存击穿
现象:
缓存击穿特指某个热点 key(高访问频率)在缓存过期的瞬间,大量并发请求同时到达,由于缓存失效,所有请求都会直接穿透到数据库,导致数据库短时间内承受巨大压力。
解决方案:
- 互斥锁(Mutex Lock) :在缓存失效时,使用分布式锁(如 Redis 的
SETNX
或RedLock
)确保只有一个请求去数据库加载数据,其余请求等待锁释放后从缓存获取最新数据。 - 永不过期(懒加载):对于热点数据,设置缓存永不过期,但通过后台定时任务或者数据变更时主动更新缓存,避免缓存失效时的并发请求冲击。
- 预加载(Refresh-ahead):在缓存即将到期时提前刷新缓存,确保新数据在旧缓存失效前已经准备好,无缝衔接,避免请求直接打到数据库。
缓存雪崩
现象:
缓存雪崩发生在大量缓存数据在同一时间段内集中过期或缓存服务整体不可用时,导致所有请求直接涌向数据库,使得数据库短时间内承受超出其处理能力的压力,可能引发服务瘫痪。
解决方案:
- 分散过期时间:为缓存设置随机的过期时间(如在原有效期内加上一个随机偏移量),避免大量缓存同时失效。
- 备份缓存:使用主从、集群等高可用架构,确保缓存服务的容错能力,单节点失效时仍能提供服务。
- 熔断与降级:在系统层面设置熔断机制,当检测到数据库压力过大时,暂时拒绝部分非核心请求或返回降级数据,保护数据库免受过载冲击。
- 限流与排队:对请求进行限流,如使用令牌桶、漏桶算法限制到达数据库的请求速率,或者将请求放入队列进行异步处理,平滑请求流量。
综上所述,应对缓存穿透、缓击穿和缓存雪崩的关键在于合理设置缓存策略、使用辅助数据结构和工具来减轻数据库压力,以及构建健壮的服务架构以应对突发流量。在面试中,除了阐述现象和解决方案,还可以讨论实际项目中如何根据业务特性和系统规模选择和实施这些策略。