Redis 缓存穿透、缓存雪崩、缓存击穿分别是什么?
以下是 Redis 缓存穿透、缓存雪崩、缓存击穿的定义及其解决方案的详细说明:
一、缓存穿透(Cache Penetration)
定义:
缓存穿透是指 查询不存在的数据,导致请求直接绕过缓存层,频繁访问数据库。通常由恶意攻击或无效请求引发。
场景示例:
- 攻击者故意请求数据库中不存在的用户 ID(如负数或极大值)。
- 缓存未命中,导致每次请求都查询数据库。
解决方案:
-
布隆过滤器(Bloom Filter):
- 原理:在缓存层前加布隆过滤器,记录所有可能存在的 Key。
- 流程:
- 查询前先检查布隆过滤器。
- 若 Key 不存在,直接返回空值。
- 若 Key 存在,再查缓存和数据库。
- 优点:内存占用小,效率高。
- 缺点:有一定误判率(需权衡误判率和内存开销)。
-
缓存空值(Cache Null):
- 原理:对数据库查不到的 Key,缓存一个空值(如
NULL
),并设置较短过期时间。 - 配置示例:
if (data == null) {redis.setex(key, 60, "NULL"); // 空值缓存 60 秒 }
- 优点:简单易实现。
- 缺点:可能缓存大量无效 Key,需定期清理。
- 原理:对数据库查不到的 Key,缓存一个空值(如
-
接口层校验:
- 原理:对请求参数进行合法性检查(如 ID 必须为正整数)。
- 示例:拦截非法参数(负数、非数字字符)直接返回错误。
二、缓存雪崩(Cache Avalanche)
定义:
缓存雪崩是指 大量缓存在同一时间过期,导致所有请求同时涌向数据库,引发数据库压力激增甚至宕机。
场景示例:
- 某电商首页商品缓存均设置为 24 小时过期,凌晨集中失效后,大量请求查询数据库。
解决方案:
-
随机过期时间:
- 原理:为缓存 Key 设置基础过期时间 + 随机偏移值。
- 示例:
int expireTime = 3600 + new Random().nextInt(600); // 3600~4200 秒 redis.setex(key, expireTime, value);
- 优点:分散缓存失效时间,降低集中访问风险。
-
热点数据永不过期:
- 原理:对核心数据不设置过期时间,通过后台任务异步更新。
- 流程:
- 缓存不设 TTL。
- 后台定时(如每隔 30 分钟)刷新缓存。
- 优点:彻底避免缓存集中失效。
- 缺点:需维护数据一致性。
-
多级缓存架构:
- 原理:采用本地缓存(如 Caffeine) + Redis 的多级缓存。
- 流程:
- 优先读取本地缓存。
- 本地缓存未命中时查询 Redis。
- Redis 未命中时查询数据库。
- 优点:分散缓存层压力,提升容错能力。
-
熔断降级:
- 原理:当数据库压力过大时,暂时拒绝部分请求。
- 工具:Hystrix、Sentinel 实现熔断机制。
三、缓存击穿(Cache Breakdown)
定义:
缓存击穿是指 某个热点 Key 在缓存过期后,大量并发请求直接访问数据库,导致数据库瞬时压力过大。
场景示例:
- 某秒杀活动的商品详情缓存过期,瞬时数万请求查询数据库。
解决方案:
-
互斥锁(Mutex Lock):
- 原理:只允许一个线程重建缓存,其他线程等待。
- 流程:
- 缓存未命中时,尝试获取分布式锁(如 Redis
SETNX
)。 - 获取锁的线程查询数据库并更新缓存。
- 其他线程等待锁释放后重试。
- 缓存未命中时,尝试获取分布式锁(如 Redis
- 示例代码:
String value = redis.get(key); if (value == null) {if (redis.setnx(lockKey, "1")) {redis.expire(lockKey, 10); // 设置锁超时时间value = db.query(...);redis.setex(key, 3600, value);redis.del(lockKey);} else {Thread.sleep(100); // 等待后重试return getData(key); } } return value;
- 优点:避免大量并发请求穿透到数据库。
- 缺点:增加系统复杂度,可能引发线程阻塞。
-
逻辑过期时间:
- 原理:缓存不设置物理过期时间,但在 Value 中存储逻辑过期时间。
- 流程:
- 缓存 Value 包含数据内容和过期时间戳(如
{"data": "...", "expire": 1672502400}
)。 - 读取时检查逻辑过期时间,若过期则异步重建缓存。
- 缓存 Value 包含数据内容和过期时间戳(如
- 优点:无需互斥锁,保证持续可用。
- 缺点:可能短暂返回旧数据。
-
热点数据预加载:
- 原理:在缓存即将过期前,提前异步刷新数据。
- 实现:结合定时任务或消息队列(如 RocketMQ)触发更新。
四、综合对比与选型建议
问题类型 | 核心区别 | 推荐方案 |
---|---|---|
缓存穿透 | 查询不存在的数据 | 布隆过滤器 + 缓存空值 |
缓存雪崩 | 大量 Key 同时失效 | 随机过期时间 + 多级缓存 |
缓存击穿 | 单个热点 Key 失效 | 互斥锁 + 逻辑过期时间 |
五、补充优化措施
- 监控与告警:
- 监控缓存命中率、数据库 QPS,设置阈值告警。
- 限流降级:
- 使用 Sentinel 对数据库查询接口限流,保护数据库。
- 数据预热:
- 高峰时段前预先加载热点数据到缓存。
- 集群化部署:
- Redis 集群 + 数据库读写分离,提升整体抗压能力。
通过结合业务场景选择合适的策略,可有效应对缓存相关问题,保障系统高可用性。