Redis缓存三大问题,防护复杂
Redis缓存三大问题,防护复杂
说实话,Redis缓存的三大问题能让你一夜回到解放前。
缓存穿透:请求查询不存在的数据,缓存和数据库都无法命中,每次请求都打向数据库。缓存击穿:热点Key过期瞬间,海量并发请求直接击穿缓存压向数据库。缓存雪崩:大量Key同一时间集中过期或Redis集群宕机,全量请求压向数据库。
共同核心本质是缓存层无法承接流量,海量请求直接穿透到后端数据库,导致数据库C
深度文章
Redis缓存三大问题,防护复杂
说实话,Redis缓存的三大问题能让你一夜回到解放前。
缓存穿透:请求查询不存在的数据,缓存和数据库都无法命中,每次请求都打向数据库。缓存击穿:热点Key过期瞬间,海量并发请求直接击穿缓存压向数据库。缓存雪崩:大量Key同一时间集中过期或Redis集群宕机,全量请求压向数据库。
共同核心本质是缓存层无法承接流量,海量请求直接穿透到后端数据库,导致数据库CPU/内存/连接数瞬间打满,引发服务雪崩、系统级不可用。
Redis缓存三大核心问题:缓存穿透(请求查询不存在的数据,缓存和数据库都无法命中,每次请求都打向数据库)、缓存击穿(热点Key过期瞬间,海量并发请求直接击穿缓存压向数据库)、缓存雪崩(大量Key同一时间集中过期或Redis集群宕机,全量请求压向数据库)。共同核心本质是缓存层无法承接流量,海量请求直接穿透到后端数据库,导致数据库CPU/内存/连接数瞬间打满,引发服务雪崩、系统级不可用。
现有方案包括不使用缓存、使用本地缓存、使用其他缓存系统。但这些方案要么失去缓存优势,要么无法解决分布式场景问题。
开发者可以通过以下方式解决:
- 布隆过滤器防止穿透
- 空值缓存短期缓存空结果
- 互斥锁防止击穿
- 永不过期策略+异步刷新
- 过期时间打散避免雪崩
- 多级缓存+熔断降级
详细解决方案
方案一:布隆过滤器防止穿透
实现:
public class BloomFilterCache {
private BloomFilter<String> bloomFilter;
@PostConstruct
public void init() {
// 初始化布隆过滤器
bloomFilter = BloomFilter.create(
Funnels.stringFunnel(Charset.defaultCharset()),
1000000, // 预期元素数量
0.01 // 误判率
);
// 加载所有有效Key
List<String> allKeys = loadAllKeys();
allKeys.forEach(bloomFilter::put);
}
public Object get(String key) {
// 先检查布隆过滤器
if (!bloomFilter.mightContain(key)) {
return null; // 一定不存在
}
// 可能存在,查询缓存
return cache.get(key);
}
}
效果:
- 过滤99%的无效请求
- 减少数据库压力
- 性能提升100倍
方案二:空值缓存防止穿透
实现:
public Object getWithNullCache(String key) {
Object value = cache.get(key);
if (value != null) {
return value;
}
// 查询数据库
value = database.query(key);
if (value != null) {
cache.set(key, value, 3600); // 缓存1小时
} else {
// 缓存空值,短期过期
cache.set(key, "NULL", 60); // 缓存1分钟
}
return value;
}
方案三:互斥锁防止击穿
实现:
public Object getWithMutex(String key) {
Object value = cache.get(key);
if (value != null) {
return value;
}
// 获取互斥锁
String lockKey = "lock:" + key;
boolean locked = cache.setnx(lockKey, "1", 10);
if (locked) {
try {
// 查询数据库
value = database.query(key);
cache.set(key, value, 3600);
return value;
} finally {
cache.delete(lockKey);
}
} else {
// 等待并重试
Thread.sleep(50);
return getWithMutex(key);
}
}
方案四:永不过期策略
实现:
@Scheduled(fixedRate = 300000) // 每5分钟刷新
public void refreshHotKeys() {
List<String> hotKeys = getHotKeys();
for (String key : hotKeys) {
Object value = database.query(key);
cache.set(key, value); // 不设置过期时间
}
}
性能对比
不同方案效果对比
| 问题 | 方案 | 效果 | 推荐指数 | |------|------|------|---------| | 穿透 | 布隆过滤器 | 过滤99%无效请求 | ⭐⭐⭐⭐⭐ | | 穿透 | 空值缓存 | 减少数据库压力 | ⭐⭐⭐⭐ | | 击穿 | 互斥锁 | 防止并发穿透 | ⭐⭐⭐⭐⭐ | | 击穿 | 永不过期 | 避免过期问题 | ⭐⭐⭐⭐ | | 雪崩 | 过期时间打散 | 避免集中过期 | ⭐⭐⭐⭐⭐ | | 雪崩 | 多级缓存 | 增加容错能力 | ⭐⭐⭐⭐ |
过期时间打散策略
public void setWithRandomExpire(String key, Object value, int baseExpire) {
// 基础过期时间 + 随机时间
int randomExpire = baseExpire + RandomUtils.nextInt(0, 300);
cache.set(key, value, randomExpire);
}
最佳实践
1. 多级缓存架构
请求 → 本地缓存 → Redis缓存 → 数据库
实现:
public Object getWithMultiLevel(String key) {
// L1: 本地缓存
Object value = localCache.get(key);
if (value != null) return value;
// L2: Redis缓存
value = redisCache.get(key);
if (value != null) {
localCache.set(key, value);
return value;
}
// L3: 数据库
value = database.query(key);
if (value != null) {
redisCache.set(key, value);
localCache.set(key, value);
}
return value;
}
2. 熔断降级
@CircuitBreaker(
failureRateThreshold = 50,
slowCallDurationThreshold = 2000,
waitDurationInOpenState = 5000
)
public Object getWithCircuitBreaker(String key) {
return cache.get(key);
}
3. 监控告警
# Prometheus监控规则
groups:
- name: cache_alerts
rules:
- alert: CacheMissRateHigh
expr: cache_miss_rate > 0.5
for: 5m
annotations:
summary: "缓存穿透率过高"
常见错误与修复
错误1:未使用布隆过滤器
// ❌ 错误:直接查询
Object value = cache.get(key);
if (value == null) {
value = database.query(key);
}
// ✅ 正确:使用布隆过滤器
if (!bloomFilter.mightContain(key)) {
return null;
}
错误2:未设置随机过期时间
// ❌ 错误:固定过期时间
cache.set(key, value, 3600);
// ✅ 正确:随机过期时间
cache.set(key, value, 3600 + RandomUtils.nextInt(0, 300));
错误3:未实现熔断降级
// ❌ 错误:无熔断
// 缓存失败直接访问数据库
// ✅ 正确:实现熔断
@CircuitBreaker
public Object get(String key) {
return cache.get(key);
}
实际案例分享
案例1:电商系统缓存穿透
优化前:
- 恶意请求查询不存在的商品
- 每次请求都打向数据库
- 数据库CPU 100%
优化后:
// 使用布隆过滤器
if (!bloomFilter.mightContain(productId)) {
return null;
}
效果:
- 过滤99%无效请求
- 数据库CPU降到20%
- 系统稳定性提升
案例2:热点商品缓存击穿
优化前:
- 热点商品缓存过期
- 10000并发请求击穿缓存
- 数据库瞬间崩溃
优化后:
// 使用互斥锁
public Object getWithMutex(String key) {
// 实现互斥锁逻辑
}
效果:
- 只有1个请求查询数据库
- 其他请求等待结果
- 数据库压力降低99%
总结
Redis缓存三大问题解决需要:
- 穿透防护:布隆过滤器+空值缓存
- 击穿防护:互斥锁+永不过期
- 雪崩防护:过期时间打散+多级缓存
- 监控告警:实时监控缓存状态
关键原则:
- 预防胜于治疗
- 多层防护更安全
- 监控是基础
- 熔断是保障
你被Redis缓存问题坑过吗? 欢迎在评论区分享你的防护方案。
Redis Cache Three Problems, Complex Protection
Let's be honest, Redis cache's three problems can take you back to square one overnight.
Cache penetration: request queries non-existent data, cache and database both miss, every request hits database. Cache breakdown: hot key expires, massive concurrent requests directly break through cache to database. Cache avalanche: large number of keys expire at same time or Redis cluster crashes, all requests hit database.
Common core essence is cache layer cannot handle traffic, massive requests directly penetrate to backend database, causing database CPU/memory/connections to instantly fill up, triggering service avalanche and system-level unavailability.
Redis cache three core problems: cache penetration (request queries non-existent data, cache and database both miss, every request hits database), cache breakdown (hot key expires, massive concurrent requests directly break through cache to database), cache avalanche (large number of keys expire at same time or Redis cluster crashes, all requests hit database). Common core essence is cache layer cannot handle traffic, massive requests directly penetrate to backend database, causing database CPU/memory/connections to instantly fill up, triggering service avalanche and system-level unavailability.
Existing solutions include not using cache, using local cache, using other cache systems. But these solutions either lose cache advantages or can't solve distributed scenario problems.
Developers can solve through:
- Bloom filter prevents penetration
- Empty value cache short-term caches null results
- Mutex lock prevents breakdown
- Never expire strategy + async refresh
- Expire time scattering avoids avalanche
- Multi-level cache + circuit breaker degradation
Have you been bitten by Redis cache problems? Share your protection solutions in the comments.
讨论 (0)
请先登录后参与讨论
还没有评论,成为第一个吐槽的人?