一、缓存穿透(Cache Penetration)
问题描述
请求 数据库中不存在的数据(如非法ID),导致请求绕过缓存直接击穿到数据库。
典型场景:
恶意攻击:频繁请求
id=-1
或随机不存在的用户ID。业务逻辑缺陷:未校验参数合法性(如非数字ID查询)。
解决方案
空值缓存
将查询结果为null
的请求也缓存,设置较短的过期时间(如 5分钟)。
public Object getData(String key) {
Object value = redis.get(key);
if (value != null) {
return value; // 命中缓存
}
// 查数据库
value = db.query(key);
if (value == null) {
redis.setex(key, 300, "NULL"); // 缓存空值
} else {
redis.setex(key, 3600, value); // 缓存真实值
}
return value;
}
布隆过滤器(Bloom Filter)
在缓存层前加布隆过滤器,判断请求的 key 是否可能存在:
存在 → 查缓存或数据库。
不存在 → 直接返回,避免查库。
接口层校验
- 对请求参数进行合法性校验(如ID范围、格式)。
二、缓存击穿(Cache Breakdown)
问题描述
热点数据过期瞬间,大量并发请求直接涌入数据库。
典型场景:
明星爆款商品详情页缓存过期。
秒杀活动中核心商品信息缓存失效。
解决方案
互斥锁(分布式锁)
只允许一个线程重建缓存,其他线程等待。
public Object getData(String key) {
Object value = redis.get(key);
if (value == null) {
String lockKey = key + "_LOCK";
if (redis.setnx(lockKey, "1", 10)) { // 获取锁
try {
value = db.query(key); // 查数据库
redis.setex(key, 3600, value);
} finally {
redis.del(lockKey); // 释放锁
}
} else {
Thread.sleep(100); // 等待后重试
return getData(key); // 递归调用
}
}
return value;
}
逻辑过期时间(永不过期 + 异步更新)
缓存不设置物理过期时间,但存储逻辑过期字段。
后台线程定期检测并更新缓存。
// 缓存数据结构
class CacheData {
Object data;
long expireTime; // 逻辑过期时间
}
public Object getData(String key) {
CacheData cacheData = redis.get(key);
if (cacheData == null) {
return db.query(key); // 首次加载
}
if (System.currentTimeMillis() > cacheData.expireTime) {
// 提交异步任务更新缓存
executor.submit(() -> {
Object newData = db.query(key);
redis.set(key, new CacheData(newData, System.currentTimeMillis() + 3600000));
});
}
return cacheData.data;
}
热点数据永不过期
对极热点数据不设置过期时间,通过异步线程定期更新。
三、缓存雪崩(Cache Avalanche)
问题描述
大量缓存同时过期 或 缓存服务宕机,导致请求全部涌入数据库。
典型场景:
缓存服务器重启后所有数据丢失。
批量缓存设置相同过期时间(如每日零点统一过期)。
解决方案
随机过期时间
在基础过期时间上增加随机值(如 基础时间 + 随机0~300秒
)。
int baseExpire = 3600;
int randomExpire = baseExpire + new Random().nextInt(300);
redis.setex(key, randomExpire, value);
集群高可用
使用 Redis 集群(Cluster)或哨兵模式(Sentinel)避免单点故障。
熔断降级
当数据库压力过大时,启用熔断机制(如返回默认值、限流)。
// 使用 Hystrix 实现熔断
@HystrixCommand(fallbackMethod = "fallbackGetData")
public Object getData(String key) {
return redis.get(key);
}
public Object fallbackGetData(String key) {
return "系统繁忙,请稍后重试"; // 降级响应
}
多级缓存
本地缓存(如 Caffeine) + Redis 缓存,降低 Redis 压力。
对比总结
问题类型 | 触发条件 | 核心解决思路 | 典型方案 |
---|---|---|---|
穿透 | 查询不存在的数据 | 拦截非法请求、缓存空值 | 布隆过滤器、空值缓存 |
击穿 | 热点数据过期 | 限制并发重建、异步更新 | 互斥锁、逻辑过期时间 |
雪崩 | 大量缓存同时失效或服务宕机 | 分散过期时间、服务高可用 | 随机过期、集群部署、熔断降级 |
实战建议
组合使用方案:
布隆过滤器 + 空值缓存 → 解决穿透。
互斥锁 + 随机过期 → 解决击穿和雪崩。
监控与预警:
监控缓存命中率、数据库 QPS,及时发现问题。
压测验证:
模拟高并发场景,验证方案的可靠性。
通过合理设计缓存策略,可显著提升系统在高并发场景下的稳定性和性能。