在现代高并发系统中,Redis 被广泛用于数据缓存,大幅提高响应速度并减轻数据库压力。然而,随着业务复杂度的提升,简单的缓存逻辑已经无法满足系统稳定性的要求。
今天我们将深入剖析 Redis 三大常见问题:缓存穿透、缓存击穿、缓存雪崩,并给出可落地的应对策略,助你构建更健壮的缓存架构。
一、缓存穿透(Cache Penetration)
✅ 定义
缓存穿透是指请求的数据在缓存中查不到,数据库中也没有,导致每次请求都绕过缓存,直击数据库。
❗ 问题危害
每次请求都访问数据库,等同于没使用缓存。
大量恶意请求可能压垮数据库,形成“缓存绕过攻击”。
📌 典型场景
请求非法 ID,如负数、UUID、空字符串等。
请求的是已删除但未更新缓存的数据。
攻击者构造大量不存在 key 的请求。
🛠 解决方案
示例代码:缓存空值
if (cached == null) {
Product product = db.get(id);
redis.set("product:" + id, product == null ? NULL_MARKER : product, 5分钟);
}
二、缓存击穿(Cache Breakdown)
✅ 定义
缓存击穿是指:某个热点 key 过期或被清除的瞬间,瞬时大量请求打入数据库,造成数据源被压垮。
❗ 问题危害
热点数据突然失效,大量请求并发访问数据库。
数据库压力激增,可能引发雪崩连锁反应。
📌 典型场景
秒杀系统中的商品详情页。
热门博文或排行榜在短时间内访问量极高。
🛠 解决方案
示例代码:互斥锁重建缓存
Product product = redis.get(key);
if (product == null) {
if (tryLock(key)) {
product = db.get(id);
redis.set(key, product, 30分钟);
releaseLock(key);
} else {
// 休眠重试
Thread.sleep(50);
return getProduct(id); // 递归尝试
}
}
三、缓存雪崩(Cache Avalanche)
✅ 定义
缓存雪崩指:大量缓存同时失效,导致所有请求穿透缓存,瞬间压垮数据库。
❗ 问题危害
数据库无法承受突如其来的高并发压力。
服务响应缓慢甚至不可用,造成大规模故障。
📌 典型场景
缓存集中过期(如同一时间重启服务,缓存全失效)。
缓存设置统一的 TTL。
🛠 解决方案
示例代码:设置随机过期时间
int ttl = 30 + new Random().nextInt(10); // 30~40 分钟
redis.set("key", value, ttl, TimeUnit.MINUTES);
四、总结对比
五、最佳实践建议
✅ 热点数据使用长 TTL 或异步刷新。
✅ 数据为空时缓存空值,防止穿透。
✅ 所有 TTL 设置加入随机因子。
✅ 接口层做好参数校验。
✅ 引入布隆过滤器提升安全性和性能。
✅ 尽可能将非关键业务异步处理,避免主流程阻塞。
六、推荐工具 & 技术组件
七、参考实现:Spring Boot 中防缓存穿透
@Service
public class UserService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private UserRepository userRepository;
public User getUserById(Long id) {
String key = "user:" + id;
Object cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
return cached instanceof NullUser ? null : (User) cached;
}
User user = userRepository.findById(id).orElse(null);
redisTemplate.opsForValue().set(key, user == null ? new NullUser() : user, Duration.ofMinutes(5));
return user;
}
private static class NullUser implements Serializable {}
}
八、结语
合理设计缓存策略,是构建高可用、高性能系统的基础保障。应对缓存穿透、击穿和雪崩问题,不能只靠一个方案,而是组合多种机制进行防护。