Redis,什么是缓存穿透/击穿/雪崩,如何解决它们

Redis,什么是缓存穿透/击穿/雪崩,如何解决它们

基于 Redis 7.0 的 图解 + 实战代码 完整方案

让你 10 分钟 搞懂 三大缓存杀手 及其 克星


目录

  1. 导语:三大杀手初印象
  2. [缓存穿透(Cache Penetration)](#缓存穿透(Cache Penetration) "#%E7%BC%93%E5%AD%98%E7%A9%BF%E9%80%8F")
  3. [缓存击穿(Cache Breakdown)](#缓存击穿(Cache Breakdown) "#%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF")
  4. [缓存雪崩(Cache Avalanche)](#缓存雪崩(Cache Avalanche) "#%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9")
  5. 对比表:一眼看懂区别
  6. 实战:Spring Boot + Redis 代码实现
  7. 压测与调优建议
  8. 总结:口诀记忆

导语:三大杀手初印象

场景 描述 危害
穿透 查询 不存在 的数据,缓存 不命中,DB 被拖垮 QPS 暴涨,DB 宕机
击穿 热点 突然失效大量并发 打到 DB DB 连接耗尽
雪崩 大量 key 同时过期Redis 宕机 整个系统崩溃

缓存穿透(Cache Penetration)

1. 流程图

sequenceDiagram participant App participant Redis participant DB App->>Redis: get user:9999 Redis->>App: nil App->>DB: select * from user where id=9999 DB->>App: null App->>Redis: set user:9999 null

2. 解决方案

方案 说明 代码片段
布隆过滤器 拦截 不存在 的 key BloomFilter.put("user:9999")
空值缓存 缓存 NULL 并设置 短过期时间 redis.setex("user:9999", 30, "NULL")

3. 布隆过滤器(RedisBloom)

java 复制代码
// 初始化
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("user-filter");
bloomFilter.tryInit(1000000, 0.03);
// 放入
bloomFilter.add("user:1");
// 判断
if (!bloomFilter.contains("user:9999")) {
    return null; // 直接返回,不查 DB
}

缓存击穿(Cache Breakdown)

1. 场景

  • 热点数据 (如秒杀商品)过期瞬间万级并发 打到 DB。

2. 解决方案

方案 说明 代码片段
互斥锁(分布式锁) 一个线程 查 DB,其余等待 RLock lock = redisson.getLock("lock:item:1")
逻辑过期(永不过期) 过期时间放在 value 里,后台异步刷新 value = {data:xxx, expire:1719999999}

3. 分布式锁实现

java 复制代码
public Item getItem(Long id) {
    String key = "item:" + id;
    Item item = redis.get(key);
    if (item != null) {
        return item; // 命中缓存
    }
    // 未命中,加锁
    RLock lock = redisson.getLock("lock:item:" + id);
    try {
        if (lock.tryLock(1, 10, TimeUnit.SECONDS)) {
            item = redis.get(key); // 双重检查
            if (item != null) return item;
            item = db.findById(id);
            redis.setex(key, 300, item); // 5 分钟过期
            return item;
        }
        // 等待后重试
        Thread.sleep(50);
        return getItem(id);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } finally {
        lock.unlock();
    }
}

缓存雪崩(Cache Avalanche)

1. 场景

  • 大量 key 同时过期 (如凌晨全量失效)→ DB 被打爆
  • Redis 宕机所有流量打到 DB

2. 解决方案

方案 说明 代码片段
随机过期时间 打散失效时间 expire = base + random(0, 600)
永不过期 + 后台刷新 逻辑过期,异步更新 value = {data:xxx, expire:xxx}
Redis 高可用 主从 + 哨兵 / Cluster spring.redis.cluster.nodes=node1:6379,node2:6379

3. 随机过期时间实现

java 复制代码
public void setWithRandomExpire(String key, Object value) {
    int base = 3600; // 1小时
    int random = new Random().nextInt(600); // 0-600秒
    redis.setex(key, base + random, value);
}

对比表:一眼看懂区别

问题 触发条件 现象 解决方案
穿透 查询 不存在 数据 DB 被 不存在 查询打爆 布隆过滤器空值缓存
击穿 热点 突然失效 DB 被 同一 key 打爆 分布式锁逻辑过期
雪崩 大量 key 同时失效 DB 被 所有 key 打爆 随机过期永不过期

实战:Spring Boot + Redis 代码实现

1. 依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.4</version>
</dependency>

2. 配置类

java 复制代码
@Configuration
public class RedisConfig {

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        return Redisson.create(config);
    }
}

3. 统一缓存模板(防穿透 + 防击穿)

java 复制代码
@Service
public class CacheService {

    @Autowired
    private RedissonClient redisson;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public <T> T get(String key, Supplier<T> dbFallback, Class<T> clazz) {
        T value = (T) redisTemplate.opsForValue().get(key);
        if (value != null) return value;

        // 布隆过滤器防穿透
        RBloomFilter<String> bloomFilter = redisson.getBloomFilter("bloom");
        bloomFilter.tryInit(1000000, 0.03);
        if (!bloomFilter.contains(key)) {
            return null;
        }

        // 分布式锁防击穿
        RLock lock = redisson.getLock("lock:" + key);
        try {
            if (lock.tryLock(1, 10, TimeUnit.SECONDS)) {
                value = (T) redisTemplate.opsForValue().get(key);
                if (value != null) return value;
                value = dbFallback.get();
                if (value == null) {
                    redisTemplate.opsForValue().set(key, "NULL", 30, TimeUnit.SECONDS);
                } else {
                    redisTemplate.opsForValue().set(key, value, 3600 + new Random().nextInt(600), TimeUnit.SECONDS);
                }
                return value;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
        return null;
    }
}

压测与调优建议

参数 建议值 说明
batch.size 32 KB 提高吞吐
linger.ms 5-20 ms 延迟换吞吐
compression.type lz4 压缩比 + CPU 平衡
fetch.min.bytes 1 KB 减少空拉取
num.network.threads CPU 核数 网络 I/O 线程

总结:口诀记忆

穿透布隆拦
击穿锁来扛
雪崩随机散
Redis 宕机高可用上

掌握 "布隆锁 + 随机散 + 高可用" 三件套,

你就拥有了 抗住百万并发终极缓存方案

相关推荐
pipip.1 小时前
Go原生高性能内存网关IMS,比Redis更快
开发语言·redis·golang
修行者Java5 小时前
Redis 核心功能全解析:功能描述、应用场景与 Java 实战
redis
xie_pin_an5 小时前
Redis 核心命令速查表
数据库·redis·缓存
苦学编程的谢5 小时前
Redis_11_类型补充+命令补充+RESP
数据库·redis·缓存
hzk的学习笔记5 小时前
Redisson 和 Jedis 的区别
数据库·redis·缓存
无心水6 小时前
【中间件:Redis】5、Redis分布式锁实战:从基础实现到Redisson高级版(避坑指南)
redis·分布式·中间件·redisson·后端面试·redis分布式锁·分布式系统
无心水6 小时前
【中间件:Redis】3、Redis数据安全机制:持久化(RDB+AOF)+事务+原子性(面试3大考点)
redis·中间件·面试·后端面试·redis事务·redis持久化·redis原子性
陈果然DeepVersion7 小时前
Java大厂面试真题:从Spring Boot到AI微服务的三轮技术拷问
spring boot·redis·微服务·ai·智能客服·java面试·rag