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 宕机高可用上

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

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

相关推荐
知我Deja_Vu3 天前
redisCommonHelper.generateCode(“GROUP“),Redis 生成码方法
数据库·redis·缓存
Charlie_lll3 天前
Redis脑裂问题处理——基于min-replicas-to-write配置
redis·后端
奇点爆破XC3 天前
Redis迁移
数据库·redis·bootstrap
断手当码农3 天前
Redis 实现分布式锁的三种方式
数据库·redis·分布式
菜鸟小九3 天前
redis原理篇(基本数据结构)
数据结构·数据库·redis
没有bug.的程序员3 天前
电商秒杀系统深度进阶:高并发流量建模、库存零超卖内核与 Redis+MQ 闭环
数据库·redis·缓存·高并发·电商秒杀·流量建模·库存零超卖
菜鸟小九3 天前
redis原理篇(五种数据结构)
数据结构·数据库·redis
初次攀爬者3 天前
Redis分布式锁实现的三种方式-基于setnx,lua脚本和Redisson
redis·分布式·后端
June`3 天前
Redis缓存深度解析:20%数据应对80%请求
数据库·redis
m0_738120723 天前
应急响应——Solar月赛emergency靶场溯源过程(内含靶机下载以及流量分析)
java·开发语言·网络·redis·web安全·系统安全