欢迎光临
我们一直在努力

Redis缓存雪崩、穿透、击穿解决方案,生产环境落地配置

只要做Java后端开发,线上服务基本都离不开Redis缓存。缓存确实能大幅提升接口QPS、降低数据库压力,但很多人只用到了基础的缓存查询、存储功能,完全忽略了缓存穿透、缓存击穿、缓存雪崩这三大经典问题。

我之前参与过电商项目线上压测,发现很多接口看似有缓存加持,一旦遇到恶意请求、热点Key过期、大批量缓存同时失效,数据库瞬间被打满,CPU飙升、接口大面积超时,严重时直接引发服务雪崩。

这三种缓存问题看着相似,但成因和解决方案完全不同。网上很多教程讲得太笼统,落地性很差。今天我结合生产真实落地经验,逐一拆解三种问题的成因、风险,配套可直接上线的代码+配置方案,帮大家彻底根治Redis缓存线上隐患。

​​​​​​​

一、快速区分三大缓存问题(通俗易懂)

很多开发工作多年,还是容易混淆三种场景,我用最直白的话总结:

1. 缓存穿透:查询不存在的数据,缓存查不到、数据库频繁查询,恶意空请求打垮DB。

2. 缓存击穿:热点Key过期瞬间,大量并发请求直接打到数据库,瞬间压垮DB。

3. 缓存雪崩:大批量缓存同时过期/Redis宕机,所有请求全部落库,整体服务瘫痪。

搞清楚场景差异,才能针对性优化,避免盲目堆砌解决方案。

二、缓存穿透:解决方案与实战代码

缓存穿透一般是用户传非法ID、爬虫恶意刷接口导致的。比如查询id=-1、随机乱数,Redis和数据库都没有数据,每次请求都会穿透到DB,毫无防护能力。

生产最优方案:空值缓存 + 布隆过滤器。小项目用空值缓存足够,高并发电商项目叠加布隆过滤器拦截无效Key。

落地代码(空值缓存)

@Service public class GoodsServiceImpl implements GoodsService { @Autowired private RedisTemplate<String, Object> redisTemplate; @Autowired private GoodsMapper goodsMapper; @Override public Goods getGoodsById(Long id) { String key = "goods:info:" + id; // 1.先查缓存 Object cacheObj = redisTemplate.opsForValue().get(key); if (cacheObj != null) { // 判断是否是空值缓存 if ("null".equals(cacheObj)) { return null; } return (Goods) cacheObj; } // 2.缓存未命中,查询数据库 Goods goods = goodsMapper.selectById(id); if (goods == null) { // 空值缓存,过期时间短,避免占用内存 redisTemplate.opsForValue().set(key, "null", 60, TimeUnit.SECONDS); } else { redisTemplate.opsForValue().set(key, goods, 30, TimeUnit.MINUTES); } return goods; } }

空值缓存可以拦截大量重复无效请求,成本极低、落地零难度,是中小型项目首选方案。

三、缓存击穿:热点Key失效并发解决方案

秒杀商品、首页热门数据这类热点Key,访问量极大。一旦缓存过期,瞬间海量并发请求直接穿透到数据库,极易导致DB瞬间卡死。

生产最优方案:互斥锁 + 热点Key永不过期,这里推荐Redisson分布式锁方案,性能和稳定性最优。

分布式锁防击穿核心代码

@Override public Goods getHotGoods(Long id) { String key = "goods:hot:" + id; String lockKey = "lock:goods:" + id; Goods goods = (Goods) redisTemplate.opsForValue().get(key); // 缓存存在直接返回 if (goods != null) { return goods; } // 缓存不存在,加分布式锁,只放行一个请求查库 RLock lock = redissonClient.getLock(lockKey); try { lock.lock(); // 二次查询缓存,防止重复查库 goods = (Goods) redisTemplate.opsForValue().get(key); if (goods != null) { return goods; } // 查询数据库回填缓存 goods = goodsMapper.selectById(id); if (goods != null) { // 热点数据可设置较长过期时间,接近永不过期 redisTemplate.opsForValue().set(key, goods, 60, TimeUnit.MINUTES); } } finally { lock.unlock(); } return goods; }

通过分布式锁控制,保证同一时刻只有一个请求查库回填缓存,彻底杜绝热点Key击穿问题。

四、缓存雪崩:批量失效终极解决配置

缓存雪崩的核心原因:大量Key设置了相同过期时间,整点批量过期,瞬间所有缓存失效,请求全部打满数据库。

最实用的解决方式:过期时间随机偏移,打散缓存过期时间,避免集中失效。同时搭配Redis高可用集群,防止单机宕机引发雪崩。

过期时间打散代码

// 基础过期时间30分钟 long baseTime = 30 * 60; // 随机偏移 0~600秒,打散批量过期 long randomTime = new Random().nextInt(600); redisTemplate.opsForValue().set(key, goods, baseTime + randomTime, TimeUnit.SECONDS);

仅仅这几行代码,就能彻底解决绝大多数缓存雪崩场景,改造量极小、收益极高。

五、生产环境完整兜底策略

除了针对性解决方案,线上项目建议统一配置兜底机制,层层防护:

1. Redis高可用:线上必须部署主从+哨兵/集群,杜绝单机故障;

2. 服务熔断降级:配合Sentinel、Resilience4j,Redis异常时直接返回默认数据,不打DB;

3. 禁止固定过期时间:所有业务缓存必须加随机偏移;

4. 热点Key监控:实时监控大Key、热Key,提前人工兜底。

六、总结

很多人把三种缓存问题混为一谈,导致优化方案堆砌、代码冗余却没解决根本问题。简单复盘核心逻辑:穿透防空查、击穿防热点、雪崩防批量失效。

本文提供的代码和配置都是经过线上验证的生产方案,轻量化、无冗余,适配90%以上的中小型后端项目。只要严格按照这套规范落地,基本可以彻底杜绝Redis缓存引发的线上故障,大幅提升服务稳定性。

赞(0)
未经允许不得转载:171主机测评 » Redis缓存雪崩、穿透、击穿解决方案,生产环境落地配置
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址