1) 【一句话结论】
缓存雪崩是缓存数据集中失效导致数据库瞬间压力激增,需通过预防(如随机过期时间)与应对(缓存预热、分布式锁、限流+熔断)多策略组合,核心是分散请求并保证数据一致性。
2) 【原理/概念讲解】
老师口吻解释:缓存雪崩是指当缓存中大量数据同时过期失效时,所有请求都绕过缓存直接访问后端数据库,导致数据库瞬间承受巨大流量,系统响应变慢甚至崩溃。类比:就像课程表缓存原计划凌晨2点统一过期,所有用户同时访问课程列表页面时,缓存失效,所有请求涌向数据库,数据库瞬间爆满,系统不可用。
- 缓存雪崩的预防:设置随机过期时间。具体来说,为每个缓存key的过期时间增加一个随机偏移量(比如原过期时间2小时,随机偏移量在-30分钟到+30分钟之间),这样每个key的失效时间会分散在原时间前后,避免集中失效。
- 缓存预热:提前将热点数据加载到缓存,避免首次访问时缓存为空,减少数据库压力。
- 分布式锁:通过锁机制保证多实例并发操作原子性,防止并发写入导致数据不一致(如缓存更新时只有一个实例执行)。
- 限流:控制请求速率,比如令牌桶算法,限制每秒请求数量,避免突发流量冲击。
- 熔断:当请求失败率超过阈值时,直接返回错误或降级,避免级联故障(如数据库访问失败率超50%时熔断)。
3) 【对比与适用场景】
- 缓存预热:定义是提前加载热点数据到缓存;特性是预先填充缓存,减少首次访问延迟;使用场景是热点数据首次访问;注意点是需定时任务(如Cron)或消息队列(如RabbitMQ)触发,避免实时加载影响性能,需平衡预热时间与资源消耗。
- 分布式锁:定义是保证多实例并发操作原子性;特性是互斥访问,确保操作一致性;使用场景是缓存更新、分布式事务;注意点是锁超时需合理(如10秒),避免死锁,锁粒度需足够细,避免资源浪费。
- 限流:定义是控制请求速率;特性是限制请求频率,防止流量过载;使用场景是高并发场景;注意点是令牌桶/漏桶算法,需考虑公平性(令牌发放速率与请求速率匹配),速率计算结合业务场景。
- 熔断:定义是故障隔离,快速失败;特性是当失败率超过阈值时,快速返回错误;使用场景是系统故障时;注意点是阈值依据历史数据(如5分钟内失败率≥50%),阈值调整机制(如熔断后逐步恢复,失败率下降则降级为半熔断)。
4) 【示例】
课程预约系统场景:假设课程表缓存(存储所有课程信息)原设置凌晨2点统一过期,用户访问课程列表页面时,缓存失效,大量请求同时访问数据库,导致数据库压力激增。
- 缓存预热:非高峰期(凌晨1点)通过定时任务(Cron表达式:0 0 1 * * ?)加载所有课程数据到Redis缓存,提前填充热点数据。
- 分布式锁:更新缓存时,使用Redis SETNX命令加锁(key为“course_cache_lock”,超时时间10秒),加锁成功后更新缓存,解锁。伪代码(含锁超时重试):
import redis
r = redis.Redis()
lock_key = "course_cache_lock"
expire_time = 10 # 秒
max_retry = 3
retry_delay = 1 # 秒
for attempt in range(max_retry):
if r.setnx(lock_key, 1, ex=expire_time):
# 更新缓存逻辑
r.set("course_table", course_data)
r.delete(lock_key) # 解锁
break
else:
time.sleep(retry_delay) # 等待后重试
- 限流:对课程列表请求设置限流(令牌桶算法,每秒发放1000个令牌,请求需获取令牌才能处理)。
- 熔断:数据库访问失败率超过50%(5分钟内失败次数≥50%),触发熔断,直接返回“课程列表加载中”错误,避免级联故障。
5) 【面试口播版答案】
面试官您好,缓存雪崩是指缓存数据集中失效导致大量请求直接访问后端数据库,导致数据库压力激增。常见解决方案包括:
- 缓存预热:提前加载热点数据到缓存,比如课程表数据在非高峰期(凌晨1点)通过定时任务加载,避免首次访问时缓存为空。
- 分布式锁:保证缓存更新时只有一个实例执行,防止并发写入导致数据不一致,比如用Redis SETNX加锁,加锁成功后更新缓存。
- 限流+熔断:限流控制请求速率(令牌桶算法),熔断在数据库失败率超阈值时快速失败,避免级联故障。
在课程预约系统中,若课程表缓存凌晨2点统一过期,用户访问时缓存失效,大量请求落库。通过缓存预热提前加载数据,分布式锁控制更新,限流熔断处理突发请求,能分散数据库压力,保证系统可用性。
6) 【追问清单】
- 缓存预热如何实现?<回答>:通过定时任务(如Cron)或消息队列(如RabbitMQ)触发,提前加载数据到缓存,避免首次访问时缓存为空。
- 分布式锁在缓存更新时如何处理?<回答>:使用Redis SETNX加锁,加锁成功后更新缓存,解锁,若加锁失败则等待后重试,避免死锁。
- 限流和熔断的区别?<回答>:限流是控制请求速率(如令牌桶),属于流量控制;熔断是故障隔离(失败率超阈值时快速失败),属于容错机制。
- 熔断阈值的设定依据?<回答>:根据历史数据统计数据库访问失败率(如5分钟内失败率超过50%则触发),阈值调整采用半熔断策略,失败率下降后逐步恢复。
- 缓存预热失败怎么办?<回答>:降级处理,从数据库读取数据,保证系统可用,避免缓存预热影响业务。
7) 【常见坑/雷区】
- 忽略缓存雪崩的预防(如随机过期时间),只说解决方案,导致对问题核心理解不完整。
- 缓存预热不提定时任务或消息队列,导致实现细节不清晰,无法体现工程落地能力。
- 分布式锁不解释锁超时设置或锁粒度,可能导致死锁或资源浪费。
- 限流和熔断混淆,无法区分两者作用(限流是流量控制,熔断是故障隔离)。
- 熔断阈值设定过于绝对,缺乏依据(如未说明失败率计算周期或阈值调整机制),可信度不足。