导读:本期聚焦于广州SEO公司创作的《Spring Boot中缓存穿透、击穿、雪崩如何模拟并彻底解决?》,敬请观看详情。当商品详情接口在秒杀场景下突然返回大量空值并拖垮数据库,往往不是代码写错,而是缓存穿透在作祟。缓存穿透指查询不存在的数据绕过缓存直击库表,击穿是热点键过期瞬间被并发打穿,雪崩则是批量键同时失效引发洪峰。本文以Spring Boot整合Redis为例,用单元测试构造三类异常流量,演示空值缓存、布隆过滤器、互斥锁与随机过期时间的具体写法。理清三者差异后,才能针对业务选对防护组合,避免线上事故。

在分布式系统里,Redis常作为Spring Boot应用的第一道防线和数据库之间的缓冲层。一旦缓存层被异常流量突破,后端MySQL或PostgreSQL就会承受超出预期的查询压力,轻则接口超时,重则实例宕机。缓存穿透、击穿、雪崩是三类机理不同但表现相似的问题,很多团队在故障复盘时才发现混淆了概念。本节先给出可运行的模拟环境搭建方式,后续再逐一拆解每种问题的复现与修复。

一、搭建可模拟问题的Spring Boot基础工程

要真实模拟三类缓存异常,需要先准备一个最简Spring Boot工程,引入Web、Data JPA、Spring Data Redis以及JUnit测试依赖。我们设计一个商品服务,通过主键查询商品信息,缓存使用Redis的String结构,过期时间统一设为五分钟。这样的结构足以在本地用多线程测试代码触发各种边界情况。

在配置文件中,除了配置Redis连接地址,还要打开JPA的SQL日志,方便观察是否发生了数据库直查。测试时我们用H2内存库代替真实MySQL,避免环境污染。下面的配置片段展示了核心参数,实际项目中可把超时与连接池调大一些以应对压测。

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.url=jdbc:h2:mem:testdb

业务类采用典型的Cache Aside模式:先查Redis,未命中再查库并回写。这个看似稳妥的写法在极端场景下就是漏洞源头。后面三节会基于同一段查询逻辑,分别注入不存在ID、热点ID过期、批量ID同时过期三种条件,让你看到问题是如何发生的。

二、缓存穿透的模拟与布隆过滤器方案

缓存穿透的本质是查询一个根本不存在的数据,比如用负数ID或伪造的UUID访问商品接口。因为Redis和数据库都没有这条记录,每次请求都会穿透到库。攻击者只需构造海量非法ID,就能用极低成本打垮数据库。我们用JUnit起二十个线程并发查ID为-1的商品,即可在日志中看到二十条相同的SELECT语句。

解决思路有两种主流做法。其一是缓存空值,将查不到的结果以空对象写入Redis并设较短过期时间,这样后续请求在缓存层就被拦截。其二是引入布隆过滤器,在请求进入业务逻辑前先判断ID是否可能存在于集合,不存在直接拒绝。下面的代码演示了空值缓存改造,注意空值过期时间要明显短于正常数据,防止无效键堆积。

public Product getProduct(Long id) {
    String cache = redisTemplate.opsForValue().get("product:" + id);
    if (cache != null) {
        if ("NULL".equals(cache)) {
            return null;
        }
        return JSON.parseObject(cache, Product.class);
    }
    Product p = productRepository.findById(id).orElse(null);
    if (p == null) {
        redisTemplate.opsForValue().set("product:" + id, "NULL", 2, TimeUnit.MINUTES);
    } else {
        redisTemplate.opsForValue().set("product:" + id, JSON.toJSONString(p), 5, TimeUnit.MINUTES);
    }
    return p;
}

布隆过滤器方案更适合ID空间巨大且非法请求占比高的场景。可以用Redisson的RBloomFilter在应用启动时把已有商品ID全部放入,查询前先调用contains方法。它的代价是存在极微小误判率,且删除困难,但在防穿透上比空值缓存更省内存。两种方案也可叠加:过滤器挡掉明显非法的,空值缓存兜住偶发漏网之鱼。

三、缓存击穿的互斥锁与逻辑过期模拟

缓存击穿发生在某个极度热点键过期的瞬间,比如首页爆款商品缓存刚好失效,此时涌进来的数千并发全部发现缓存未命中,一齐去查库并回写,数据库瞬间被压垮。我们用单测让一百个线程同时查同一个存在的热点ID,并在查询前手动删除该ID的缓存,就能复现这一状况。

最常见的解法是互斥锁:第一个线程拿到锁去查库并回写,其余线程等待或短暂休眠后重试。Spring Boot里可以用Redis的setIfAbsent配合过期时间实现分布式锁。下面的示例展示了加锁逻辑,注意锁必须设超时防止死锁,且释放时要校验持有者避免误删。

public Product getHotProduct(Long id) {
    String key = "product:" + id;
    String cache = redisTemplate.opsForValue().get(key);
    if (cache != null) {
        return JSON.parseObject(cache, Product.class);
    }
    String lockKey = "lock:" + key;
    Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
    if (Boolean.TRUE.equals(locked)) {
        try {
            Product p = productRepository.findById(id).orElse(null);
            redisTemplate.opsForValue().set(key, JSON.toJSONString(p), 5, TimeUnit.MINUTES);
            return p;
        } finally {
            redisTemplate.delete(lockKey);
        }
    } else {
        try { Thread.sleep(50); } catch (InterruptedException e) {}
        return getHotProduct(id);
    }
}

另一种思路叫逻辑过期,即Redis键不设置物理TTL,而在值里存入过期时间戳,业务线程发现逻辑过期后异步刷新。这种做法读请求永不阻塞,但实现复杂度高,且在刷新完成前可能返回旧数据。对于一致性要求不极端的场景,互斥锁已经足够清晰可控,也是多数团队的首选。

四、缓存雪崩的随机过期与多级缓存防护

缓存雪崩是指大量键在同一时刻集体失效,或者Redis实例宕机,导致请求像雪崩一样全部砸向数据库。我们在测试中可以初始化一千个商品并统一设五分钟过期,然后用定时任务模拟五分钟后批量失效,紧接着发起混合查询,即可看到数据库连接池被瞬间占满的告警。

预防雪崩的核心是打散过期时间。给每个键的基础TTL加上随机偏移量,比如五分钟加减三十秒,就能避免集中失效。同时,对关键业务引入本地缓存(如Caffeine)作为二级缓存,即使Redis全挂,本地还能扛住部分流量。下面代码展示了随机过期时间的写法,注意随机范围要根据数据总量调整。

int base = 5 * 60;
int random = new Random().nextInt(60) - 30;
redisTemplate.opsForValue().set(key, value, base + random, TimeUnit.SECONDS);

除了过期打散,高可用架构也不可忽视。生产环境应使用Redis Cluster或哨兵模式,保证单节点故障能自动切换。配合熔断组件(如Resilience4j)在数据库压力过大时快速失败,能进一步缩小故障半径。雪崩不是单点技术问题,而是缓存层、应用层、架构层共同协防的结果,只有组合多种手段才能做到真正稳妥。

Spring_Boot缓存穿透缓存雪崩修改时间:2026-08-17 15:56:37

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。