news 2026/5/2 21:12:10

SpringBoot+Redis实战:手把手教你用黑马点评项目搞定缓存穿透、击穿、雪崩三大难题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+Redis实战:手把手教你用黑马点评项目搞定缓存穿透、击穿、雪崩三大难题

SpringBoot+Redis实战:破解高并发场景下的缓存三连击

Redis作为现代分布式系统的缓存利器,其性能优势毋庸置疑。但当缓存机制设计不当,系统便会遭遇缓存穿透、击穿、雪崩这三大致命问题。本文将基于黑马点评项目的实战经验,深入剖析这三种典型缓存问题的成因与解决方案。

1. 缓存穿透:当查询穿透到数据库

缓存穿透是指查询一个必然不存在的数据,由于缓存未命中,请求直接穿透到数据库层。恶意攻击者可能利用此漏洞发起大量无效查询,导致数据库不堪重负。

1.1 空对象缓存策略

黑马点评项目中的CacheClient类实现了空对象缓存机制:

public <R,ID> R queryWithPassThrough( String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallback, Long time, TimeUnit unit){ String key = keyPrefix + id; String json = stringRedisTemplate.opsForValue().get(key); if (StrUtil.isNotBlank(json)){ return JSONUtil.toBean(json, type); } if (json != null){ // 命中空值 return null; } R r = dbFallback.apply(id); if (r == null){ // 缓存空对象,设置较短过期时间 stringRedisTemplate.opsForValue().set( key, "", CACHE_NULL_TTL, TimeUnit.MINUTES); return null; } this.set(key, r, time, unit); return r; }

关键实现点:

  • 对数据库查询结果为null的情况,仍然缓存空字符串
  • 为空的缓存设置较短的TTL(如2-5分钟),避免长期占用内存
  • 下次相同查询直接返回null,避免穿透到数据库

1.2 布隆过滤器增强方案

对于海量数据场景,可结合布隆过滤器进行前置过滤:

方案内存占用误判率实现复杂度
空对象缓存较高
布隆过滤器可配置

布隆过滤器虽然存在一定的误判率,但其内存效率极高。实际项目中可根据业务特点选择组合方案。

2. 缓存击穿:热点数据突然失效

当某个热点key过期瞬间,大量并发请求直接打到数据库,这就是缓存击穿。黑马点评采用逻辑过期+互斥锁双重保障机制。

2.1 逻辑过期设计

首先定义包含逻辑过期时间的Redis数据结构:

@Data public class RedisData { private LocalDateTime expireTime; private Object data; }

对应的缓存写入方法:

public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit){ RedisData redisData = new RedisData(); redisData.setData(value); redisData.setExpireTime(LocalDateTime.now() .plusSeconds(unit.toSeconds(time))); stringRedisTemplate.opsForValue().set( key, JSONUtil.toJsonStr(redisData)); }

2.2 互斥锁实现缓存重建

当检测到逻辑过期时,通过互斥锁控制只有一个线程进行缓存重建:

private boolean tryLock(String key){ Boolean flag = stringRedisTemplate.opsForValue() .setIfAbsent(key, "1", 10, TimeUnit.SECONDS); return BooleanUtil.isTrue(flag); } private void unlock(String key){ stringRedisTemplate.delete(key); }

缓存查询的核心逻辑:

public <R,ID> R queryWithLogicalExpire(...){ // ... if (expireTime.isAfter(LocalDateTime.now())){ return r; } String lockKey = LOCK_SHOP_KEY + id; boolean isLock = tryLock(lockKey); if (isLock){ // 异步重建缓存 CACHE_REBUILD_EXECUTOR.submit(()->{ try { R r1 = dbFallback.apply(id); this.setWithLogicalExpire(key, r1, time, unit); } finally { unlock(lockKey); } }); } return r; }

最佳实践建议:

  • 锁的过期时间要短于缓存重建时间
  • 使用线程池异步重建避免阻塞主线程
  • 返回旧数据保证可用性,牺牲短暂一致性

3. 缓存雪崩:大规模缓存失效

当大量缓存同时过期或Redis宕机,请求洪峰直接冲击数据库,这就是缓存雪崩。黑马点评项目采用多级防御策略。

3.1 差异化过期时间

基础防护是为缓存设置随机过期时间:

// 基础过期时间 + 随机偏移量 long expireTime = BASE_TTL + RandomUtil.randomLong(5, 60); stringRedisTemplate.opsForValue().set( key, value, expireTime, TimeUnit.MINUTES);

3.2 多级缓存架构

进阶方案是构建多级缓存体系:

用户请求 → Nginx本地缓存 → Redis集群 → JVM缓存 → 数据库

各级缓存配置建议:

缓存层级过期时间特点
Nginx1-5s应对瞬时高峰
Redis5-30min主缓存层
JVM1-2min进程级备份

3.3 熔断降级机制

配置Hystrix等熔断工具,当数据库压力过大时自动降级:

@HystrixCommand( fallbackMethod = "getShopInfoFallback", commandProperties = { @HystrixProperty( name="circuitBreaker.requestVolumeThreshold", value="20"), @HystrixProperty( name="circuitBreaker.sleepWindowInMilliseconds", value="10000") } ) public Shop getShopInfo(Long id) { // 正常业务逻辑 }

4. 实战:优惠券秒杀中的缓存设计

黑马点评的秒杀模块完美融合了上述缓存策略,其核心流程如下:

  1. 库存预热:活动前将库存加载到Redis
  2. 资格校验:Lua脚本原子性扣减库存
  3. 订单处理:异步队列削峰填谷

库存扣减的Lua脚本:

local stockKey = 'seckill:stock:'..voucherId local orderKey = 'seckill:order:'..voucherId if (tonumber(redis.call('get', stockKey)) <= 0) then return 1 end if (redis.call('sismember', orderKey, userId) == 1) then return 2 end redis.call('incrby', stockKey, -1) redis.call('sadd', orderKey, userId) return 0

秒杀系统的缓存要点:

  • 库存数据永不过期(逻辑删除)
  • 用户订单记录设置长TTL
  • 使用Redis事务保证原子性
  • 异步写库减轻数据库压力

5. 性能优化与监控

完善的缓存系统需要配套的监控措施:

5.1 关键指标监控

通过Prometheus监控以下核心指标:

redis_hit_rate{instance="cache01"} 0.98 redis_latency_ms{op="get"} 12.3 cache_penetration_count{type="null"} 42

5.2 缓存预热策略

系统启动时自动加载热点数据:

@PostConstruct public void initHotData() { List<Long> hotShopIds = shopMapper.selectHotShopIds(); hotShopIds.forEach(id -> { Shop shop = shopMapper.selectById(id); cacheClient.setWithLogicalExpire( CACHE_SHOP_KEY + id, shop, CACHE_SHOP_TTL, TimeUnit.MINUTES); }); }

5.3 动态调整策略

基于监控数据自动优化缓存配置:

// 根据命中率动态调整TTL if (hitRate < 0.9) { cacheConfig.setDefaultTtl( cacheConfig.getDefaultTtl() * 1.5); } else if (hitRate > 0.98) { cacheConfig.setDefaultTtl( Math.max(300, cacheConfig.getDefaultTtl() * 0.8)); }

在实际项目中,缓存策略需要根据业务特点灵活调整。黑马点评的CacheClient类提供了良好的基础封装,开发者可以基于此扩展更适合自己业务的缓存组件。记住:没有放之四海皆准的缓存方案,只有最适合当前场景的解决方案。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/2 21:06:30

AI命令交互前端运行时:流式输出与会话恢复的图形化解决方案

1. 项目概述&#xff1a;一个为AI命令交互而生的前端运行时 如果你是一名开发者&#xff0c;或者经常需要和各种AI模型、API打交道&#xff0c;那么你一定对这样的场景不陌生&#xff1a;打开一个终端&#xff0c;运行一个脚本&#xff0c;然后盯着那个黑漆漆的窗口&#xff0c…

作者头像 李华
网站建设 2026/5/2 21:01:38

如何用Cura实现专业级3D打印切片:从新手到专家的完整指南

如何用Cura实现专业级3D打印切片&#xff1a;从新手到专家的完整指南 【免费下载链接】Cura 项目地址: https://gitcode.com/gh_mirrors/cur/Cura 你是否曾经面对复杂的3D模型文件感到无从下手&#xff1f;是否在寻找一款能够将创意想法快速转化为实体打印的终极工具&a…

作者头像 李华
网站建设 2026/5/2 20:55:58

快速原型开发中如何利用Taotoken分钟级接入多个大模型进行效果验证

快速原型开发中如何利用Taotoken分钟级接入多个大模型进行效果验证 1. 统一接入的价值与场景 在AI应用原型开发阶段&#xff0c;技术团队常面临模型选型难题。不同模型在特定任务上的表现差异显著&#xff0c;传统方式需要为每个目标模型单独注册账号、申请API权限、阅读不同…

作者头像 李华
网站建设 2026/5/2 20:39:23

WorkshopDL:跨平台轻量级Steam创意工坊模组下载器终极指南

WorkshopDL&#xff1a;跨平台轻量级Steam创意工坊模组下载器终极指南 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否在GOG或Epic平台购买游戏后&#xff0c;发现无法访…

作者头像 李华