当前位置: 代码网 > it编程>数据库>Redis > Redis之缓存击穿、穿透、雪崩问题及处理

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

2026年04月27日 Redis 我要评论
一、缓存击穿(一)概念某个热点 key 过期的瞬间,大量并发请求同时打到数据库,导致数据库压力瞬间飙升,甚至被打崩。大量并发请求 ---> 访问同一个热点 key

一、缓存击穿

(一)概念

某个热点 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 和数据库瞬时压力

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com