缓存穿透雪崩击穿,数据库压力骤增
缓存穿透雪崩击穿,数据库压力骤增
你有没有遇到过这种场景:系统突然变慢,数据库CPU飙升到100%;发现大量请求穿透缓存直达数据库;或者缓存同时失效,瞬间大量请求打到数据库导致崩溃。
这就是缓存使用的三大典型问题:缓存穿透、缓存雪崩、缓存击穿。
可二次开发的解决方案
好消息是,这些问题都可以通过二次开发解决:
1. 缓存穿透 - 布隆过滤器
使用布隆过滤器拦截无
深度文章
缓存穿透雪崩击穿,数据库压力骤增
你有没有遇到过这种场景:系统突然变慢,数据库CPU飙升到100%;发现大量请求穿透缓存直达数据库;或者缓存同时失效,瞬间大量请求打到数据库导致崩溃。
这就是缓存使用的三大典型问题:缓存穿透、缓存雪崩、缓存击穿。
可二次开发的解决方案
好消息是,这些问题都可以通过二次开发解决:
1. 缓存穿透 - 布隆过滤器
使用布隆过滤器拦截无效Key:
@Bean
public BloomFilter<String> bloomFilter() {
return BloomFilter.create(
Funnels.stringFunnel(Charset.defaultCharset()),
1000000, // 预期元素数量
0.01 // 误判率
);
}
public Object getData(String key) {
// 先检查布隆过滤器
if (!bloomFilter.mightContain(key)) {
return null; // 一定不存在
}
// 查询缓存和数据库
return cache.get(key, () -> db.query(key));
}
2. 缓存雪崩 - 过期时间随机化
避免大量Key同时过期:
public void setCache(String key, Object value) {
// 基础过期时间 + 随机偏移
long baseExpire = 3600; // 1小时
long randomExpire = ThreadLocalRandom.current().nextLong(0, 600);
cache.setex(key, baseExpire + randomExpire, value);
}
3. 缓存击穿 - 互斥锁
使用分布式锁防止并发重建:
public Object getData(String key) {
Object value = cache.get(key);
if (value == null) {
// 获取分布式锁
String lockKey = "lock:" + key;
try {
if (redisLock.tryLock(lockKey, 10, TimeUnit.SECONDS)) {
// 双重检查
value = cache.get(key);
if (value == null) {
value = db.query(key);
cache.set(key, value, 3600);
}
}
} finally {
redisLock.unlock(lockKey);
}
}
return value;
}
4. 多级缓存
构建多级缓存架构:
// L1: 本地缓存 (Caffeine)
// L2: 分布式缓存 (Redis)
// L3: 数据库 (MySQL)
public Object getData(String key) {
// 先查本地缓存
Object value = localCache.getIfPresent(key);
if (value != null) return value;
// 再查分布式缓存
value = redisCache.get(key);
if (value != null) {
localCache.put(key, value);
return value;
}
// 最后查数据库
value = db.query(key);
if (value != null) {
redisCache.set(key, value);
localCache.put(key, value);
}
return value;
}
解决方案对比
| 问题 | 解决方案 | 效果 | |------|---------|------| | 缓存穿透 | 布隆过滤器 + 空值缓存 | 拦截99%无效请求 | | 缓存雪崩 | 过期时间随机化 + 多级缓存 | 避免同时失效 | | 缓存击穿 | 互斥锁 + 异步刷新 | 防止并发重建 |
Cache Penetration Avalanche Breakdown, Database Pressure Surges
Have you encountered this scenario: system suddenly slows down, database CPU spikes to 100%; find massive requests penetrate cache directly to database; or cache fails simultaneously, instantly massive requests hit database causing crash.
These are three typical cache problems: cache penetration, cache avalanche, cache breakdown.
Developer Solutions
Good news is, these problems can all be solved through secondary development:
1. Cache Penetration - Bloom Filter
Use Bloom filter to intercept invalid keys:
@Bean
public BloomFilter<String> bloomFilter() {
return BloomFilter.create(
Funnels.stringFunnel(Charset.defaultCharset()),
1000000, // Expected insertions
0.01 // False positive probability
);
}
public Object getData(String key) {
// Check bloom filter first
if (!bloomFilter.mightContain(key)) {
return null; // Definitely not present
}
// Query cache and database
return cache.get(key, () -> db.query(key));
}
2. Cache Avalanche - Random Expiration
Avoid large number of keys expiring simultaneously:
public void setCache(String key, Object value) {
// Base expiration + random offset
long baseExpire = 3600; // 1 hour
long randomExpire = ThreadLocalRandom.current().nextLong(0, 600);
cache.setex(key, baseExpire + randomExpire, value);
}
3. Cache Breakdown - Mutex Lock
Use distributed lock to prevent concurrent rebuild:
public Object getData(String key) {
Object value = cache.get(key);
if (value == null) {
// Acquire distributed lock
String lockKey = "lock:" + key;
try {
if (redisLock.tryLock(lockKey, 10, TimeUnit.SECONDS)) {
// Double check
value = cache.get(key);
if (value == null) {
value = db.query(key);
cache.set(key, value, 3600);
}
}
} finally {
redisLock.unlock(lockKey);
}
}
return value;
}
4. Multi-level Cache
Build multi-level cache architecture:
// L1: Local cache (Caffeine)
// L2: Distributed cache (Redis)
// L3: Database (MySQL)
public Object getData(String key) {
// Check local cache first
Object value = localCache.getIfPresent(key);
if (value != null) return value;
// Check distributed cache
value = redisCache.get(key);
if (value != null) {
localCache.put(key, value);
return value;
}
// Finally check database
value = db.query(key);
if (value != null) {
redisCache.set(key, value);
localCache.put(key, value);
}
return value;
}
Solution Comparison
| Problem | Solution | Effect | |---------|----------|--------| | Cache Penetration | Bloom filter + Empty value cache | Intercept 99% invalid requests | | Cache Avalanche | Random expiration + Multi-level cache | Avoid simultaneous failure | | Cache Breakdown | Mutex lock + Async refresh | Prevent concurrent rebuild |
讨论 (0)
请先登录后参与讨论
还没有评论,成为第一个吐槽的人?