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

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

一、缓存击穿

(一)概念

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

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

(二)缓存击穿的后果

1. 数据库压力瞬间飙升

  • 热点 Key 过期后,大量并发请求直接落到数据库
  • 数据库瞬间承受超高并发请求,CPU、IO 占用飙升

2. 系统性能下降

  • 数据库响应变慢 → 接口响应延迟增加

3. 请求可能超时或失败

  • 高并发下,整体系统吞吐量下降

4. 可能触发雪崩效应

  • 一个热点 Key 击穿导致数据库压力过大
  • 可能影响其他业务请求
  • 形成连锁反应,多个 Key 失效 → 系统整体性能下降

5. 运维风险增加

  • 数据库连接耗尽、事务阻塞
  • CPU/内存占用过高 → 可能导致服务宕机
  • 需要紧急干预,影响业务连续性

(三)触发条件

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

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

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

具体示例:

  • 电商网站的某个秒杀商品库存信息
  • 热门文章或新闻详情页
  • 排行榜数据,如“本周最热商品 Top 10”

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

2. Key 过期或被淘汰

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

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

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

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

具体示例:

  • 设置 SET product_123 100 EX 60,60 秒后过期
  • 服务器更新产品信息,删除缓存强制刷新
  • Redis 内存满了,大对象或低频 Key 被淘汰

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

3. 高并发访问

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

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

具体示例:

  • 秒杀活动开始时,几千甚至上万用户同时访问同一商品库存
  • 热点文章推送后,瞬间大量用户点击访问

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

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

解释:

  • 热点 Key 不存在 → 就算并发很高,也不会击穿数据库(普通 Key 的请求量本来就小)
  • Key 没有过期或被淘汰 → 缓存命中,所有请求都打到 Redis,不会落到数据库
  • 高并发访问 不存在 → 即使 Key 过期,也只有少量请求访问数据库,数据库能轻松承受
  • 所以 三个条件必须同时满足,缓存击穿才会发生。

(四)典型场景

1. 热门商品详情

场景描述:

  • 电商平台的秒杀商品或促销商品在短时间内被大量用户访问
  • 每个用户都要查询库存、价格、折扣等信息

为什么容易击穿:

  • 热点 Key:这个商品的缓存是热点,因为秒杀活动期间访问量极高
  • Key 过期或缓存未命中:TTL 到期,或者缓存被手动刷新
  • 高并发请求:几千甚至上万用户同时访问数据库查询库存

技术后果:

  • 数据库瞬时压力飙升
  • 查询延迟增加 → 可能出现秒杀失败或页面崩溃

2. 排行榜或统计数据

场景描述:

  • 热门文章阅读量、音乐/视频播放排行榜、实时交易额统计
  • 大量用户同时查询“Top N”数据

为什么容易击穿:

  • 热点 Key:排行榜数据被频繁访问
  • Key 过期:排行榜缓存每隔一段时间刷新一次,TTL 到期或逻辑过期
  • 高并发访问:缓存过期瞬间,所有请求直接打数据库计算排行

技术后果:

  • 数据库需要进行复杂聚合计算,CPU/IO 占用高
  • 多个 Key 可能同时被访问 → 加剧系统压力

3. 系统配置或元数据

场景描述:

  • 经常查询的基础信息,例如用户角色权限、地区列表、字典表数据
  • 访问频率高,但数据量相对固定

为什么容易击穿:

  • 热点 Key:这些 Key 被多次访问
  • Key 过期或被刷新:配置更新或 TTL 到期
  • 高并发访问:短时间内多个服务/用户请求这些基础数据

技术后果:

  • 一旦缓存失效,基础数据请求直接打数据库
  • 影响整个业务链条的正常访问

(五)根本原因

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

  • Redis 的主要作用是加速数据访问,减少数据库压力
  • Redis 并不会阻止数据库本身被访问
  • 一旦缓存未命中(Key 过期或被淘汰),请求就直接打数据库

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

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

  • 当一个热点 Key 过期或被删除,瞬间所有访问这个 Key 的请求都会落到数据库
  • 数据库承受能力有限,如果瞬时 QPS 超过数据库峰值 → 查询排队

技术点:

  • Redis hit rate 高 → 大部分请求在缓存层就被拦截
  • Key 失效瞬间 → 缓存失效窗口,hit rate 突然变低 → DB 瞬时压力飙升

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

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

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

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

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

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

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

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

(六)解决策略

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

思路:

  • 对真正热点的数据,不设置 TTL,让缓存一直存在
  • 或者使用“逻辑过期”,即缓存中记录一个过期时间,但读取时仍然可以返回旧数据,同时后台异步刷新

技术实现:

  • 永不过期:SET key value 不设置 EX 参数
  • 逻辑过期:缓存结构如 {value: ..., expireTime: 1234567890}
  • 读取时判断 expireTime 是否过期
  • 如果过期,后台线程异步更新缓存,用户仍然能读取旧值

适用场景:

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

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. 缓存预热 + 定时刷新

思路:

  • 系统启动或缓存即将过期时提前加载热点数据到缓存
  • 避免缓存失效瞬间出现大量请求落到数据库

技术实现:

  • 缓存预热:系统启动时读取数据库,将热点 Key 加入 Redis
  • 定时刷新:使用定时任务或后台线程,提前更新即将过期的 Key

适用场景:

  • 系统启动后的热门数据
  • 高频访问的排行榜或统计数据

4. 降级处理 / 限流

思路:

  • 当缓存击穿瞬间,可以对部分请求做降级或限流,保护数据库
  • 部分请求直接返回默认值或提示稍后再试

技术实现:

  • 使用限流器(如令牌桶、漏桶算法)限制数据库访问频率
  • 对热点查询返回缓存的旧值或默认值
  • 结合互斥锁或逻辑过期,提高系统容错能力

适用场景:

  • 秒杀活动、热门接口请求暴增场景
  • 保护数据库稳定,保证系统可用性

(七)优化角度

1. 热点识别

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

具体做法:

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

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

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

作用:

  • 只针对真正的热点做优化,避免资源浪费
  • 降低缓存击穿风险

2. TTL 差异化

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

具体做法:

  • 对普通 Key 设置短 TTL
  • 对热点 Key 设置长 TTL 或逻辑过期
  • 可以给相似热点 Key 设置错开 TTL,避免同时失效

作用:

  • 避免多个热点 Key 同时过期 → 大量请求同时打数据库
  • 平滑系统压力

3. 分布式锁 / 单点加载

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

具体做法:

  • 使用 Redis 的 SETNX 或 Redisson 提供的分布式锁
  • 第一个请求获取锁去加载数据库
  • 其他请求等待锁释放或读取缓存

作用:

  • 避免高并发请求同时击穿数据库
  • 控制数据库瞬时压力

示例流程:

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

4. 逻辑过期 + 异步刷新

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

具体做法:

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

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

  • 未过期 → 直接返回缓存
  • 已过期 → 后台线程异步刷新缓存,前端仍返回旧值

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

作用:

  • 用户请求不会直接落到数据库
  • 避免缓存失效瞬间击穿数据库
  • 保证系统高并发下可用性

(八)注意事项

1. 分布式系统注意点

  • 在多节点或微服务环境下,如果采用 分布式锁 或 逻辑过期刷新,需要保证跨节点的一致性。
  • 可使用成熟的工具或框架实现分布式锁,例如 Redisson、ZooKeeper 或 Etcd。
  • 这样可以避免多个节点同时刷新缓存,防止数据库压力再次集中。

2. 数据一致性问题

  • 使用 逻辑过期 + 异步刷新 时,可能会在短时间内返回过期的旧数据(脏数据)。
  • 需要根据业务可容忍度判断是否可以接受这种短暂的不一致,例如:排行榜、商品浏览量等统计类数据通常允许短暂延迟。
  • 对于对实时性要求高的关键业务,需额外设计一致性策略。

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

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

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

这样可以确保:

  • 请求不会直接击穿数据库
  • 缓存刷新有序
  • 系统在瞬时高并发下仍保持可用性

二、缓存穿透

(一)概念

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

  • 核心:请求的数据在缓存和数据库中都不存在
  • 表现:缓存永远无法命中,数据库承受全部请求
请求数据A(不存在)
       ↓
Redis查询,未命中
       ↓
直接访问数据库
       ↓
数据库也没有该数据
       ↓
返回结果给用户

特点:

  • 数据不存在 → 每次请求都要查询数据库
  • 缓存没有命中 → 每次都落到数据库
  • 高并发下可能造成数据库压力过大

(二)触发原因

1. 用户恶意请求

  • 恶意刷接口,随机生成不存在的 ID 请求
  • 例如:/product/999999999,数据库中根本没有

2. 程序缺陷或参数错误

  • 前端或接口调用传错参数,导致请求不存在的数据
  • 例如:用户传了非法商品 ID 或拼写错误的关键字

3. 缓存未处理空结果

  • 查询不存在的数据,缓存层没有保存空对象
  • 每次请求都走数据库 → 穿透

(三)典型场景

1. 商品查询接口

  • 用户请求不存在的商品 ID
  • 没有缓存空对象 → 每次都打数据库

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

  • 查询不存在的用户名或手机号
  • 高并发时可能形成数据库压力

3. 通用搜索或统计接口

  • 查询历史不存在的记录或随机关键字
  • 数据库承载能力下降

(四)可能后果

1. 数据库压力增加

  • 大量不存在的数据请求直接落到数据库
  • CPU、IO 占用增加

2. 接口响应变慢 / 超时

  • 高并发下数据库响应慢,接口可能返回超时

3. 系统可用性降低

  • 数据库过载 → 其他正常请求也受影响

4. 潜在安全风险

  • 恶意请求可能导致拒绝服务(DoS)攻击

(五)解决策略

1. 缓存空对象(Null Cache)

  • 将数据库查询不存在的数据也缓存起来(空对象或特殊标记)
  • 设置较短 TTL 避免缓存无限膨胀

示例:

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)

  • 在请求到达缓存/数据库之前,用布隆过滤器快速判断 key 是否存在
  • 不存在的请求直接拦截,避免落到数据库

特点:

  • 支持海量数据
  • 允许少量误判(存在概率误判为存在,但不会漏判)
  • 场景:热门商品 ID、用户 ID 等高频接口

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

  • 对传入请求参数做合法性校验
  • 非法 ID、空 ID、格式错误的请求直接拒绝

4. 限流和防刷策略

  • 对接口增加限流、频率限制
  • 防止恶意请求或高并发重复查询不存在数据

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

1. 分布式环境注意点

  • 如果系统是多节点或微服务架构,需要保证布隆过滤器或缓存空对象在各节点的一致性。
  • 可以使用 集中式布隆过滤器 或分布式缓存同步策略,避免不同节点出现数据不一致。

2. 空对象缓存策略优化

  • 缓存空对象时需要控制 TTL,避免占用过多缓存资源。
  • 对于热门但不存在的数据,可以适当加长 TTL;对于随机或恶意请求的数据,TTL 设置短一些即可。
  • 结合日志监控,发现频繁穿透的 Key,可进一步处理或封禁请求源。

3. 结合限流与防刷策略

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

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

  • 限制接口每秒请求次数
  • 对异常频繁请求 IP 或用户进行封禁或验证码验证
  • 结合缓存策略可以更有效保护数据库稳定性。

4. 监控与报警

  • 对缓存命中率、数据库查询量进行监控
  • 当发现缓存命中率下降、数据库异常增多时,触发告警
  • 帮助运维及时发现缓存穿透攻击或异常请求

三、缓存雪崩

(一)概念

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

  • 核心:大量缓存同时过期或失效
  • 表现:数据库压力骤增,接口响应延迟或失败

流程示意:

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

特点:

  • 大量 Key 同时失效 → 大量请求集中落到数据库
  • 高并发下数据库无法承受 → 系统可能整体不可用

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

(二)触发原因

1. 大量 Key TTL 相同

  • 系统初始化时给缓存设置了统一过期时间,例如都 1 小时
  • 到期瞬间,大量请求同时访问数据库

2. Redis 宕机或重启

  • Redis 服务不可用 → 缓存全部失效
  • 所有请求直接落到数据库

3. 缓存淘汰策略触发

  • 当 Redis 内存不足,根据 LRU/LFU 策略淘汰大量 Key
  • 瞬时失效 → 请求直接访问数据库

(三)典型场景

1. 秒杀活动或促销活动

  • 活动前预热缓存,设置统一过期时间
  • 活动开始或 TTL 到期 → 大量商品缓存同时过期
  • 大量请求直接打数据库 → 宕机风险

2. 系统启动或 Redis 重启

  • 系统重启或 Redis 重启 → 缓存清空
  • 热门 Key 未预热 → 请求直接落数据库

3. 大规模缓存失效

  • 一些批量刷新或缓存清理操作
  • 例如定时清理排行榜、统计数据
  • 多个 Key 同时失效 → 请求瞬间集中

(四)可能后果

1. 数据库瞬时压力飙升

  • CPU、IO、连接数暴涨

2. 接口响应延迟或失败

  • 请求排队 → 用户体验差

3. 系统可用性下降

  • 多个业务模块同时受影响 → 链式反应

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

  • 一个 Key 的高并发击穿 + 大量 Key 同时过期 → 系统雪崩

(五)解决策略

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

  • 给 Key TTL 增加随机偏移量,避免大量 Key 同时过期
  • 例如:设置 TTL = 3600 ± random(0~300) 秒

2. 缓存预热(Cache Preload)

  • 系统启动或缓存即将失效前,提前加载热点数据到缓存
  • 避免大量请求落到数据库

3. 互斥锁或单线程加载

  • 当缓存失效时,控制只有一个请求访问数据库,其它请求等待
  • 避免高并发直接打数据库

4. 降级与限流

  • 对部分请求返回默认值或提示稍后重试
  • 使用限流器控制访问数据库的速率

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

  • 本地缓存存热点数据 → Redis 失效前可以命中本地缓存
  • 降低对 Redis 和数据库瞬时压力

总结

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

相关文章

  • 利用Redis实现防止接口重复提交功能

    利用Redis实现防止接口重复提交功能

    大家好,本篇文章主要讲的是利用Redis实现防止接口重复提交功能,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • Redis分布式缓存与秒杀

    Redis分布式缓存与秒杀

    这篇文章主要介绍了Redis分布式缓存与秒杀,单点Redis的问题,主要有数据丢失,并发能力,故障恢复,存储能力,想进一步了解的同学,可以借鉴本文
    2023-04-04
  • Redis常用的数据结构及实际应用场景

    Redis常用的数据结构及实际应用场景

    本文介绍了Redis中常用的数据结构,包括字符串、列表、集合、哈希表、有序集合和Bitmap,并详细说明了它们在各种场景下的使用,需要的朋友可以参考下
    2024-05-05
  • Redis存储经纬度信息的实现

    Redis存储经纬度信息的实现

    在一些向用户提供天气信息的业务场景中,我们通常会通过前端获取用户经纬度信息,传递给后端作为参数进行外部天气接口调用,本文就来详细的介绍一下Redis存储经纬度信息的实现,感兴趣的可以了解一下
    2025-10-10
  • 拦截Redis命令导致的Lua脚本执行失败的问题解决

    拦截Redis命令导致的Lua脚本执行失败的问题解决

    本文主要介绍了拦截Redis命令导致的Lua脚本执行失败的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • Redis中StringRedisTemplate中HashOperations的使用详解

    Redis中StringRedisTemplate中HashOperations的使用详解

    文章介绍了Spring Boot 2中使用Lettuce框架访问Redis的基本步骤和常用操作,包括添加依赖、注入`StringRedisTemplate`实例以及进行字符串、哈希、集合、列表和有序集合的基本操作,文章还提供了一个登录案例,并总结了个人经验,鼓励读者参考和支持
    2026-03-03
  • Redis操作命令总结

    Redis操作命令总结

    这篇文章主要介绍了Redis操作命令总结,本文讲解了key pattern 查询相应的key、字符串类型的操作、链表操作、hashes类型及操作、集合结构操作、有序集合、服务器相关命令等内容,需要的朋友可以参考下
    2015-03-03
  • Redis教程(一):Redis简介

    Redis教程(一):Redis简介

    这篇文章主要介绍了Redis教程(一):Redis简介,本文是系列文章的第一篇,欢迎大家跟随本教程学习Redis数据库,需要的朋友可以参考下
    2015-04-04
  • Redis Cluster的图文讲解

    Redis Cluster的图文讲解

    今天小编就为大家分享一篇关于Redis Cluster的图文讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • redis远程连接不上的解决办法

    redis远程连接不上的解决办法

    本文主要介绍了redis远程连接不上的解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05

最新评论