Redis 缓存雪崩、穿透、击穿面试题深度解析与 Spring Boot 实战代码示例

本文面向准备面试、或希望梳理缓存机制及防护方案的开发者,结合 Spring Boot 提供可直接落地的代码方案。


✅ 一、缓存雪崩

1.1 什么是缓存雪崩?

大量缓存同时过期,大量请求打到数据库,造成雪崩效应。

1.2 场景模拟

例如你设置所有缓存 TTL 为 30 分钟,恰好同时失效。

1.3 解决方案

✅ 方案1:过期时间加随机

java 复制代码
redisTemplate.opsForValue().set("key", value, 30 + new Random().nextInt(30), TimeUnit.MINUTES);

✅ 方案2:提前预热、定时刷新

使用定时任务刷新热点数据:

java 复制代码
@Scheduled(cron = "0 0/10 * * * ?")
public void preloadHotData() {
    List<Data> hotData = dbService.getHotData();
    hotData.forEach(data -> redisTemplate.opsForValue().set("hot:" + data.getId(), data, 1, TimeUnit.HOURS));
}

🚧 二、缓存穿透

2.1 什么是缓存穿透?

用户查询的数据缓存没有,数据库也没有,每次都打到数据库。攻击者可借此发起攻击。

2.2 防御方案

✅ 方案1:缓存空对象

java 复制代码
Object data = redisTemplate.opsForValue().get(key);
if (data == null) {
    data = dbService.queryById(id);
    redisTemplate.opsForValue().set(key, data == null ? "null" : data, 5, TimeUnit.MINUTES);
}

✅ 方案2:布隆过滤器

java 复制代码
BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(), 50000);

@PostConstruct
public void init() {
    List<Integer> ids = dbService.getAllIds();
    ids.forEach(filter::put);
}

// 查询前判断是否存在
if (!filter.mightContain(id)) return null;

⚡ 三、缓存击穿

3.1 什么是缓存击穿?

某个热点数据刚好失效,大量并发请求直接打到数据库。

3.2 防御方案

✅ 方案1:本地锁(简单粗暴)

java 复制代码
synchronized (key.intern()) {
    data = redisTemplate.opsForValue().get(key);
    if (data == null) {
        data = dbService.query(id);
        redisTemplate.opsForValue().set(key, data, 10, TimeUnit.MINUTES);
    }
}

✅ 方案2:Redisson + 逻辑过期 + 异步更新

java 复制代码
@Data
public class CacheData<T> {
    private T data;
    private long expireTime; // 时间戳
}

public void setWithLogicExpire(String key, Object value, long time, TimeUnit unit) {
    CacheData<Object> cd = new CacheData<>();
    cd.setData(value);
    cd.setExpireTime(System.currentTimeMillis() + unit.toMillis(time));
    redisTemplate.opsForValue().set(key, cd);
}

// 异步刷新
if (System.currentTimeMillis() > cache.getExpireTime()) {
    if (lock.tryLock()) {
        executor.submit(() -> {
            Object newData = dbService.query(id);
            setWithLogicExpire(key, newData, 10, TimeUnit.MINUTES);
            lock.unlock();
        });
    }
}

🎯 四、面试问答锦集

  • 缓存雪崩、穿透、击穿的区别?
  • 如何判断线上是雪崩还是击穿?
  • Redisson 的异步刷新能解决哪些问题?
  • 缓存预热策略有哪些?

✅ 总结

场景 特征 防御策略
雪崩 大量缓存同时过期 过期时间加随机、定时预热
穿透 缓存 & DB 都没有 布隆过滤器、空对象缓存
击穿 某热点失效 本地锁、Redisson逻辑过期

🧠 推荐将缓存封装为统一服务类,便于复用和维护。

相关推荐
AM越.14 小时前
Java设计模式超详解--观察者设计模式
java·开发语言·设计模式
专注VB编程开发20年14 小时前
c#语法和java相差多少
java·开发语言·microsoft·c#
有一个好名字14 小时前
设计模式-单例模式
java·单例模式·设计模式
2301_7973122614 小时前
学习Java26天
java·开发语言
cike_y14 小时前
JSP原理详解
java·开发语言·jsp
invicinble14 小时前
关于springboot引入traceid来保障可观测型
java·spring boot·后端
精神病不行计算机不上班15 小时前
[Java Web]在IDEA中完整实现Servlet的示例
java·servlet·tomcat·html·intellij-idea·web
chushiyunen15 小时前
javadoc规范、idea生成javadoc等
java·ide
小羊学伽瓦15 小时前
ThreadLocal
java·jvm·算法
Tjohn915 小时前
Java环境配置(JDK8环境变量配置)补充
java·开发语言