当面试官问到“Redis缓存击穿了,怎么办?”时,可以围绕以下核心解决方案进行回答,并结合实际场景说明适用性:
1. 互斥锁方案(Mutex Lock)核心思路:通过分布式锁控制并发请求,确保同一时间只有一个线程能查询数据库并重建缓存。实现步骤:
- 发现缓存失效时,先尝试获取锁(如Redis的SETNX或Redisson分布式锁)。
- 获取锁成功后,再次检查缓存(避免其他线程已重建),若仍失效则查询数据库并更新缓存。
- 释放锁,未获取锁的线程可短暂重试或返回降级数据。代码示例:
public String getData(String key) { String value = redis.get(key); if (value == null) { if (tryLock(key)) { // 获取锁 try { value = redis.get(key); // 双重检查 if (value == null) { value = db.query(key); redis.set(key, value, expireTime); } } finally { unlock(key); // 释放锁 } } else { Thread.sleep(50); // 重试或降级 return getData(key); } } return value;}适用场景:高并发读多写少,且能接受短暂锁等待的系统。注意点:需设置锁超时时间,避免死锁;锁粒度不宜过大(如按数据Key加锁)。
2. 热点数据永不过期核心思路:对热点数据不设置TTL,通过后台异步线程定期更新缓存。实现方式:
- 写入缓存时不设置过期时间(如redis.set(key, value))。
- 启动定时任务(如每5分钟)从数据库加载最新数据并更新缓存。代码示例:
public void setHotKey(String key, String value) { redis.set(key, value); // 不设过期时间 scheduler.scheduleAtFixedRate(() -> { String newValue = db.query(key); redis.set(key, newValue); }, 0, 5, TimeUnit.MINUTES);}适用场景:数据更新频率低且访问量极大的场景(如秒杀商品库存)。风险点:后台任务失败会导致数据不一致,需配合监控告警;可增加版本号校验。
3. 布隆过滤器(Bloom Filter)核心思路:用布隆过滤器快速过滤无效请求,避免缓存穿透(查询不存在的Key)。实现步骤:
- 初始化时将所有存在的Key存入布隆过滤器。
- 查询前先检查过滤器,若Key不存在则直接返回空。代码示例:
public String getData(String key) { if (!bloomFilter.mightContain(key)) { // 快速判断 return null; } String value = redis.get(key); if (value == null) { value = db.query(key); if (value != null) { redis.set(key, value); } } return value;}适用场景:防止缓存穿透(大量查询不存在的Key)。局限性:存在误判率(可能误判存在),需根据业务调整过滤器参数(如哈希函数数量、位数组大小)。
4. 缓存预热核心思路:系统启动或低峰期提前加载热点数据到缓存,避免冷启动时缓存失效。实现方式:
- 通过分析日志或预定义规则确定热点Key。
- 在服务启动时或定时任务中批量加载数据到Redis。代码示例:
public void warmUpCache() { List<String> hotKeys = getHotKeys(); // 获取热点Key列表 hotKeys.parallelStream().forEach(key -> { String value = db.query(key); redis.set(key, value); });}适用场景:数据可预测且访问模式固定的系统(如电商首页推荐)。注意点:预热时机需避开高峰期,避免影响正常请求。
5. 随机过期时间核心思路:为缓存设置基础过期时间+随机偏移量,避免大量Key同时失效。实现方式:
- 基础过期时间(如30分钟)加上随机值(如0-15分钟)。代码示例:
public void setWithRandomExpire(String key, String value) { int baseExpire = 1800; // 30分钟 int randomOffset = new Random().nextInt(900); // 0-15分钟 redis.set(key, value, baseExpire + randomOffset);}适用场景:通用性方案,适合无明显热点但需分散失效时间的场景。优势:实现简单,能有效缓解集中过期导致的击穿。
综合建议- 组合使用:例如互斥锁+随机过期时间,或布隆过滤器+缓存预热。
- 监控与降级:实时监控缓存命中率,设置熔断机制(如连续失败N次后直接返回空)。
- 数据一致性:根据业务容忍度选择最终一致性(异步更新)或强一致性(加锁同步)。
通过以上方案,可以系统性解决Redis缓存击穿问题,具体选择需结合业务并发量、数据更新频率和一致性要求。