Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redis之缓存击穿、穿透、雪崩

Redis之缓存击穿、穿透、雪崩问题及处理

作者:小满、

文章详细介绍了缓存击穿、缓存穿透和缓存雪崩的概念、触发条件、典型场景、可能后果及解决策略,强调了缓存设计中需考虑的多个方面,包括缓存策略、系统一致性、异常处理和优化措施,通过合理设置TTL、使用分布式锁、预热缓存、逻辑过期刷新、布隆过滤器等多种方法

一、缓存击穿

(一)概念

某个热点 Key 过期的瞬间,大量并发请求同时打到数据库,导致数据库压力瞬间飙升,甚至被打崩。

大量并发请求  ---> 访问同一个热点 key  
                  ↓  
             这个 key 正好过期  
                  ↓  
     所有请求同时绕过 Redis 访问数据库  
                  ↓  
           DB 瞬间压力过大(被打爆)

(二)缓存击穿的后果

1. 数据库压力瞬间飙升

2. 系统性能下降

3. 请求可能超时或失败

4. 可能触发雪崩效应

5. 运维风险增加

(三)触发条件

1. 热点 Key(高访问频率)

概念:热点 Key 是指在短时间内被大量请求访问的数据。

特征:访问量远高于其他普通 Key,可能占据系统绝大部分流量。

具体示例:

为什么关键:只有热点数据过期,才会有大量请求瞬间打到数据库,引发压力。

2. Key 过期或被淘汰

概念:缓存中存放的 Key 可能由于以下原因失效:

(1)TTL 到期:设置了过期时间,到时间就失效

(2)手动删除:开发或运维手动清理缓存

(3)内存淘汰:Redis 达到内存上限,根据 LRU/LFU 策略淘汰 Key

具体示例:

为什么关键:Key 过期或消失后,下一次请求会直接落到数据库,这才是击穿的直接触发点。

3. 高并发访问

概念:短时间内大量请求同时访问同一个 Key。

特征:瞬时并发量远高于数据库处理能力。

具体示例:

为什么关键:如果并发量很小,即使 Key 过期,数据库也能承受压力;只有高并发访问,才会引发真正的缓存击穿。

4. 少了任意一个条件,就不会发生缓存击穿

解释:

(四)典型场景

1. 热门商品详情

场景描述:

为什么容易击穿:

技术后果:

2. 排行榜或统计数据

场景描述:

为什么容易击穿:

技术后果:

3. 系统配置或元数据

场景描述:

为什么容易击穿:

技术后果:

(五)根本原因

1. Redis 只是缓存,并非数据库防护墙

技术点:缓存只是读写加速层,它不存储业务逻辑约束,也不限制请求流量

2. 高并发请求集中在失效 Key 上

技术点:

3. 数据库无法承受大量并发请求

数据库在高并发下可能出现:

(1)CPU 饱和 → 查询速度下降

(2)IO 瓶颈 → 磁盘或网络访问慢

(3)连接耗尽 → 数据库拒绝新连接

(3)事务阻塞 → 并发写操作阻塞其他请求

结果:接口响应变慢、请求超时,甚至数据库宕机

技术点:缓存击穿不是 Redis 的问题,而是热点数据失效 + 数据库瞬时承载能力不足的系统问题

(六)解决策略

1. 热点 Key 永不过期 / 逻辑过期

思路:

技术实现:

适用场景:

热门商品、排行榜、系统配置、常用字典数据

2. 互斥锁 / 单线程加载

思路:

技术实现(Java 示例):

String cache = redis.get(key);
if (cache == null) {
    if (tryLock(key + "_lock")) {        // 尝试获取锁
        String dbData = queryFromDB();   // 查询数据库
        redis.set(key, dbData, 60);      // 回写缓存
        unlock(key + "_lock");           // 释放锁
        return dbData;
    } else {
        Thread.sleep(50);                // 等待一段时间
        return redis.get(key);           // 重试读取缓存
    }
}
return cache;

适用场景:

秒杀活动、热点 Key 高并发访问场景

3. 缓存预热 + 定时刷新

思路:

技术实现:

适用场景:

4. 降级处理 / 限流

思路:

技术实现:

适用场景:

(七)优化角度

1. 热点识别

概念:先识别哪些 Key 是真正的热点数据(访问频率高、压力大的 Key),针对这些 Key 才做特殊处理。

具体做法:

(1)在 Redis 中记录访问频率或使用监控统计访问量

(2)根据访问量排序,识别访问量大的 Key 为热点

(3)对热点 Key 可采取“永不过期”或“逻辑过期 + 异步刷新”等策略

作用:

2. TTL 差异化

概念:给不同 Key 设置不同过期时间,避免大量热点 Key 同时过期。

具体做法:

作用:

3. 分布式锁 / 单点加载

概念:缓存失效时,保证只有一个请求去访问数据库加载数据,其他请求等待或重试缓存。

具体做法:

作用:

示例流程:

请求1 → 获取锁 → 查询 DB → 回写缓存 → 释放锁
请求2 → 获取锁失败 → 等待 / 重试缓存
请求3 → 同上

4. 逻辑过期 + 异步刷新

概念:缓存中存储数据的逻辑过期时间,而不是直接让 Key 过期。

具体做法:

(1)缓存结构:{value: ..., expireTime: timestamp}

(2)读取时判断 expireTime 是否过期

(3)异步刷新完成后更新缓存

作用:

(八)注意事项

1. 分布式系统注意点

2. 数据一致性问题

3. 限流/降级策略的组合使用

在高并发场景下,单独的策略可能不足以完全保护数据库。

推荐组合方案:逻辑过期 + 分布式锁 + 限流/降级

这样可以确保:

二、缓存穿透

(一)概念

缓存穿透指的是 查询一个根本不存在的数据时,请求会绕过缓存直接打到数据库,如果这种请求量很大,会导致数据库压力急剧增加。

请求数据A(不存在)
       ↓
Redis查询,未命中
       ↓
直接访问数据库
       ↓
数据库也没有该数据
       ↓
返回结果给用户

特点:

(二)触发原因

1. 用户恶意请求

2. 程序缺陷或参数错误

3. 缓存未处理空结果

(三)典型场景

1. 商品查询接口

2. 用户登录或注册校验接口

3. 通用搜索或统计接口

(四)可能后果

1. 数据库压力增加

2. 接口响应变慢 / 超时

3. 系统可用性降低

4. 潜在安全风险

(五)解决策略

1. 缓存空对象(Null Cache)

示例:

String cache = redis.get(key);
if (cache != null) {
    return cache.equals("NULL") ? null : cache;
} else {
    String dbData = queryFromDB();
    if (dbData == null) {
        redis.set(key, "NULL", 60); // 缓存空对象
        return null;
    } else {
        redis.set(key, dbData, 3600);
        return dbData;
    }
}

2. 布隆过滤器(Bloom Filter)

特点:

3. 接口层校验 / 参数校验

4. 限流和防刷策略

(六)进一步优化与注意事项

1. 分布式环境注意点

2. 空对象缓存策略优化

3. 结合限流与防刷策略

在高并发或恶意请求场景,单靠缓存空对象或布隆过滤器可能不足。

可以使用 限流 + 防刷,例如:

4. 监控与报警

三、缓存雪崩

(一)概念

缓存雪崩指的是大量缓存同时失效,导致大量请求直接打到数据库,使数据库瞬间承受超高压力,可能导致系统不可用。

流程示意:

大量热点Key同时过期
       ↓
缓存全部未命中
       ↓
请求全部打到数据库
       ↓
数据库承受高压
       ↓
接口响应变慢或宕机

特点:

不同于缓存击穿:缓存雪崩可能涉及 多个 Key,而击穿通常是 单个热点 Key

(二)触发原因

1. 大量 Key TTL 相同

2. Redis 宕机或重启

3. 缓存淘汰策略触发

(三)典型场景

1. 秒杀活动或促销活动

2. 系统启动或 Redis 重启

3. 大规模缓存失效

(四)可能后果

1. 数据库瞬时压力飙升

2. 接口响应延迟或失败

3. 系统可用性下降

4. 触发缓存击穿或雪崩放大

(五)解决策略

1. 缓存过期时间错开 / 随机化 TTL

2. 缓存预热(Cache Preload)

3. 互斥锁或单线程加载

4. 降级与限流

5. 多级缓存 / 本地缓存 + Redis

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文