← 返回首页
😣
痛苦

缓存穿透雪崩击穿,数据库压力骤增

缓存后端开发

缓存穿透雪崩击穿,数据库压力骤增

你有没有遇到过这种场景:系统突然变慢,数据库CPU飙升到100%;发现大量请求穿透缓存直达数据库;或者缓存同时失效,瞬间大量请求打到数据库导致崩溃。

这就是缓存使用的三大典型问题:缓存穿透、缓存雪崩、缓存击穿

可二次开发的解决方案

好消息是,这些问题都可以通过二次开发解决:

1. 缓存穿透 - 布隆过滤器

使用布隆过滤器拦截无

深度文章

人工审核2026年5月18日

缓存穿透雪崩击穿,数据库压力骤增

你有没有遇到过这种场景:系统突然变慢,数据库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 |

2026年5月17日

讨论 (0)

请先登录后参与讨论

还没有评论,成为第一个吐槽的人?