当前位置: 首页 > news >正文

redis缓存三大问题分析与解决方案

什么是缓存?

缓存(Cache)是一种将热点数据缓存在内存中(如 Redis)以加快访问速度、减轻数据库压力的技术。

但引入缓存后可能出现 三大核心问题

  • 缓存穿透(Cache Penetration)
  • 缓存击穿(Cache Breakdown)
  • 缓存雪崩(Cache Avalanche)

一、缓存穿透(Cache Penetration)

问题描述

缓存穿透指:请求的数据既不在缓存中,也不在数据库中,导致请求每次都打到数据库

常见场景

  • 恶意攻击:传入大量随机 ID,绕过缓存层直击数据库
  • 用户访问非法 ID,如 /user?id=-1

举例

用户频繁访问一个 不存在的商品 ID:99999999

  • Redis 无此数据 → 查询数据库
  • 数据库无 → 返回 null
  • 下次再次请求 ID=99999999,又重复上述过程 → DB 被压垮

解决方案

1. 缓存空值
if (dbData == null) {redis.set("shop:99999999", "", 2分钟);
}
  • 空值也缓存,避免重复查数据库
  • 设较短 TTL(避免缓存过期数据太久)
2. 参数校验拦截非法请求
  • 如:ID 不能为负数或超过最大值
  • 在请求层面做过滤,不进 DB 或 Redis
3. 布隆过滤器(适用于大数据量)
  • 将所有合法 ID 加入布隆过滤器
  • 请求前先判断是否命中布隆过滤器,不在则直接拒绝

二、缓存击穿(Cache Breakdown)

问题描述

缓存击穿指:某个热点 Key 刚好失效时,大量并发请求打到数据库,导致数据库瞬时压力激增。

常见场景

  • 热点数据正好在高峰期过期
  • 比如:商品详情页、秒杀商品、抢购库存

举例

商品 ID=1 每天百万访问量,缓存过期瞬间,大量用户同时访问导致:

  • Redis 查不到 → 并发查询 DB → 数据库压力飙升

解决方案

1. 互斥锁方式:单线程缓存重建
if (redis.get("shop:1") == null) {if (tryLock("lock:shop:1")) {// 从 DB 读取 → 缓存写回 Redisunlock();} else {// 其他线程等待或返回默认值}
}
  • 缓存重建交给首个拿到锁的线程,其它线程等待或快速失败
2. 逻辑过期 + 异步重建(推荐)
{"data": {...},"expireTime": "2025-06-30 12:00:00"
}
  • 缓存提前设置一个逻辑过期时间(保存在 value 中)
  • 判断已过期 → 异步线程后台刷新 → 返回旧数据不中断用户体验

适合热点数据缓存更新


三、缓存雪崩(Cache Avalanche)

问题描述

大量缓存同时过期,导致所有请求同时访问数据库,引发系统雪崩。

常见场景

  • 设置了相同 TTL 的大量缓存同时过期
  • Redis 重启或崩溃,缓存瞬间全部丢失

举例

  • 秒杀系统中 10 万商品都设置 TTL=24小时
  • 恰好第二天凌晨失效 → 所有请求打到数据库

解决方案

1. 缓存过期时间加随机
int ttl = 3600 + RandomUtil.randomInt(0, 600);
redis.set("shop:" + id, value, ttl, TimeUnit.SECONDS);
  • 避免所有 key 同一时间过期,均匀错开时间点
2. 热点数据永不过期 + 后台异步刷新
  • 逻辑过期方案 + 后台定时更新
  • 热点数据维持高可用
3. 多级缓存(本地 + 分布式)
  • 如:Caffeine + Redis + MySQL 三层缓存
  • Redis 崩溃时,先从本地缓存兜底
4. 限流+降级
  • 接口层加限流、熔断、降级返回默认值,避免雪崩扩大化

项目中 Redis 缓存策略总结

问题定义解决方案
缓存穿透请求数据既不在缓存也不在数据库缓存空值、参数校验、布隆过滤器
缓存击穿热点 key 在高并发下刚好失效加锁互斥、逻辑过期 + 异步刷新
缓存雪崩大量 key 同时过期、或 Redis 故障加 TTL 随机值、热点永不过期、多级缓存、限流降级

实战建议

  • 所有缓存数据 务必设置 TTL,默认不要永久存在
  • 区分冷数据(短 TTL)与热点数据(长 TTL 或逻辑过期)
  • 高并发业务使用异步线程池或消息队列缓冲请求
  • 建立统一的缓存封装组件(CacheClient),集中处理这些问题
http://www.dtcms.com/a/266177.html

相关文章:

  • 车载以太网都有什么协议?
  • 创建 TransactionStatus
  • 【STM32实践篇】:I2C驱动编写
  • NumPy 安装使用教程
  • Debian-10-standard用`networking`服务的`/etc/network/interfaces`配置文件设置多网卡多IPv6
  • 【2.4 漫画SpringBoot实战】
  • CMake之CMakeLists.txt语法规则
  • 网安系列【1】:黑客思维、技术与案例解析
  • DDD实战:CQRS模式在电商报表系统中的高性能实践
  • RNN案例人名分类器(完整步骤)
  • MySQL 8.0 OCP 1Z0-908 题目解析(17)
  • POST请求url放参数场景-笔记
  • Spring SseEmitter 系统详细讲解
  • WPF学习笔记(16)树控件TreeView与数据模板
  • WPF学习笔记(22)项面板模板ltemsPanelTemplate与三种模板总结
  • spring-ai-alibaba 1.0.0.2 学习(八)——接入阿里云信息查询服务
  • 深度学习-逻辑回归
  • RJ45 连接器(水晶头)的引脚定义
  • 从0到1解锁Element-Plus组件二次封装El-Dialog动态调用
  • Gemini CLI初体验
  • 二叉树题解——二叉树的层序遍历【LeetCode】队列实现
  • Java中Stream流的使用
  • Web攻防-文件上传黑白名单MIMEJS前端执行权限编码解析OSS存储分域名应用场景
  • 设计模式(九)
  • 魔术方法__call__
  • Redis缓存架构实战
  • Selenium Base全新升级版:新一代自动化框架实战解析
  • Python 的内置函数 range
  • 高边驱动 低边驱动
  • 黑暗中的爆破(船讯网Ais爬虫暨爬虫实战js逆向学习经验分享)