本文面向准备面试、或希望梳理缓存机制及防护方案的开发者,结合 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逻辑过期 |
🧠 推荐将缓存封装为统一服务类,便于复用和维护。