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

【Redis】缓存击穿、缓存穿透、缓存雪崩的解决方案

【Redis】缓存击穿、缓存穿透、缓存雪崩的解决方案

    • 缓存击穿:高并发下热点 Key 失效
      • 1. 方案一:互斥锁(分布式锁)
        • 执行流程:
        • 优缺点:
      • 2. 方案二:逻辑过期
        • 执行流程:
        • 优缺点:
      • 3. 方案三:中间件/框架方案
    • 缓存穿透:不存在的 Key 冲击数据库
      • 1. 方案一:请求参数校验与拦截
      • 2. 方案二:缓存空值
        • 执行流程:
        • 注意事项:
      • 3. 方案三:布隆过滤器(Bloom Filter)
        • 核心原理:
        • 优缺点:
    • 缓存雪崩:大量 Key 失效或 Redis 宕机
      • 1. 针对“大量 Key 同时过期”的解决方案
        • 方案一:过期时间加随机值
        • 方案二:核心数据永不过期
        • 方案三:热点数据缓存预热
      • 2. 针对“Redis 整体不可用”的解决方案
        • 方案一:Redis 高可用架构
        • 方案二:多机房容灾
        • 方案三:服务兜底措施
    • 总结

在高并发业务中,Redis 缓存虽能大幅减轻数据库压力,但也会面临“缓存击穿”“缓存穿透”“缓存雪崩”三大典型问题——一旦爆发,可能导致数据库瞬间过载甚至宕机。

缓存击穿:高并发下热点 Key 失效

缓存击穿的核心场景是:某个被高并发访问的“热点 Key”突然过期失效,此时大量请求会绕过缓存直接冲击数据库,导致数据库压力骤增,甚至被压垮。

1. 方案一:互斥锁(分布式锁)

通过分布式锁保证“同一时间只有一个请求能访问数据库并更新缓存”,其他请求需等待锁释放后再从缓存获取数据,本质是“限流削峰”。

执行流程:
  1. 请求查询缓存,若 Key 失效(缓存 miss),则尝试获取分布式锁(如 Redis 的 SET NX 命令);
  2. 若获取锁成功:访问数据库查询数据,将数据写入缓存(并设置新的过期时间),最后释放锁;
  3. 若获取锁失败:短暂休眠(如 50ms)后重新查询缓存,循环直到获取到数据;
  4. 优化:加入“双重检测”——获取锁后再次检查缓存是否已存在数据,避免因锁等待期间其他请求已更新缓存导致的重复操作。
优缺点:
  • 优势:数据一致性强(数据库更新后立即同步缓存),逻辑简单;
  • 劣势:存在锁等待阻塞,高并发下可能增加请求延迟,且需处理分布式锁的死锁、释放异常等问题。

2. 方案二:逻辑过期

不设置缓存 Key 的实际过期时间(让 Key 永不过期),而是在缓存数据内部嵌入“逻辑过期时间”。请求时通过判断逻辑时间决定是否异步更新缓存,避免阻塞。

执行流程:
  1. 缓存热点数据时,在 Value 中加入 expireTime(如 {data: "xxx", expireTime: 1699999999}),且 Key 不设置过期时间;
  2. 请求查询缓存时,先判断 expireTime 是否过期:
    • 若未过期:直接返回数据;
    • 若已过期:尝试获取分布式锁,获取成功后开启异步线程(如线程池),去数据库更新数据并刷新缓存的 expireTime,同时释放锁;
    • 未获取到锁:直接返回旧数据(牺牲短暂一致性,保证性能)。
优缺点:
  • 优势:无请求阻塞,性能极高,适合对数据一致性要求不严格的场景(如商品销量、实时榜单);
  • 劣势:数据存在“短暂不一致”(过期后到异步更新完成前,返回旧数据)。

3. 方案三:中间件/框架方案

部分大厂会通过定制化中间件或框架,优化锁的性能开销,典型案例如下:

  • 某手 Cache Setter
    基于“一致性哈希”将相同 Key 的请求路由到同一台 Cache Setter 服务器,服务器内部用 ConcurrentHashMap 维护“Key-CountDownLatch”映射。当某 Key 首次缓存 miss 时,创建 CountDownLatch 并存储到 Map,同时去数据库更新缓存;后续请求查询到该 Key 对应的 Latch 时,会调用 await() 阻塞,直到第一个请求更新完缓存后调用 countDown(),所有阻塞请求唤醒并从缓存获取数据。
    核心优势:避免分布式锁的网络开销,用本地锁+Latch 实现“单节点内串行更新”,性能更优。

  • 某东 Jd HotKey
    通过专门的热点 Key 检测系统,实时识别业务中的热点 Key,将这些 Key 同步到应用本地缓存(如 Caffeine)。当请求到来时,优先查询本地缓存,避免 Redis 热点 Key 失效后的数据库冲击。
    核心优势:本地缓存无网络延迟,且绕开了 Redis 层面的 Key 失效问题。

缓存穿透:不存在的 Key 冲击数据库

缓存穿透的核心场景是:请求查询的 Key 在缓存和数据库中都不存在(如恶意构造的无效 ID、不存在的用户信息),导致所有请求直接穿透缓存访问数据库,长期积累可能压垮数据库。

1. 方案一:请求参数校验与拦截

在请求到达缓存/数据库前,通过“网关层”或“业务层”拦截无效请求,从源头减少穿透。

  • 参数合法性校验:对请求参数做规则校验(如用户 ID 必须为正整数、订单号格式必须符合规则),非法参数直接返回 400;
  • IP/用户限流/黑名单:对高频发送无效请求的 IP 或用户(如每秒请求超 100 次),通过网关限流(如 Sentinel、Nginx 限流)或加入黑名单,短期内禁止其访问。

核心逻辑:在“缓存前”拦截无效请求,避免无效流量进入后续链路。

2. 方案二:缓存空值

对数据库中不存在的 Key,在缓存中存储“空值”(如 null""),并设置较短的过期时间(如 5-10 分钟),避免后续相同请求重复穿透。

执行流程:
  1. 请求查询 Key,缓存 miss 后访问数据库;
  2. 若数据库查询结果为空(数据不存在),则在缓存中写入 Key -> 空值,并设置短期过期时间;
  3. 后续相同 Key 的请求会从缓存获取空值,直接返回,无需访问数据库。
注意事项:
  • 过期时间不能过长:避免数据库后续新增该 Key 时,缓存的空值导致“数据不一致”;
  • 需区分“真空”和“业务空”:如“用户未下单”属于业务空,可缓存;“参数非法”属于无效请求,应直接拦截,无需缓存。

3. 方案三:布隆过滤器(Bloom Filter)

当请求的“不存在 Key”数量极多(如恶意构造百万级无效 ID),缓存空值会导致缓存膨胀,此时需用布隆过滤器做“前置存在性判断”——仅允许“可能存在”的数据进入缓存/数据库链路。

核心原理:

在这里插入图片描述

  • 写入阶段:数据库新增数据时,将 Key 通过多个哈希函数计算出多个哈希值,对应到布隆过滤器的“比特数组”中,将这些位置的比特位设为 1;
  • 查询阶段:请求到来时,先将 Key 通过相同哈希函数计算,若所有对应比特位均为 1,说明 Key“可能存在”(允许继续查缓存/数据库);若有任一比特位为 0,说明 Key“一定不存在”(直接返回,无需穿透)。
优缺点:
  • 优势:占用内存极小(比特级存储),判断速度快(O(k),k 为哈希函数个数),适合海量 Key 的存在性判断;
  • 劣势:存在“误判率”(可能将不存在的 Key 判断为存在),且不支持删除操作(删除一个 Key 会影响其他 Key 的判断)。

缓存雪崩:大量 Key 失效或 Redis 宕机

缓存雪崩的核心场景有两类:

  1. 大量 Key 同时过期:同一时间大量缓存 Key 失效,请求集中冲击数据库;
  2. Redis 整体不可用:Redis 宕机、内存溢出导致热点数据被淘汰,或机房故障等不可抗力,导致缓存完全失效,所有请求回源数据库。

1. 针对“大量 Key 同时过期”的解决方案

方案一:过期时间加随机值

在设置 Key 的固定过期时间(如 1 小时)基础上,额外叠加一个随机值(如 0-300 秒),让 Key 的实际过期时间分散在“1 小时 ~ 1 小时 5 分钟”之间,避免大量 Key 同一时间失效。

示例代码逻辑:

// 固定过期时间 3600 秒,叠加 0-300 秒随机值
int baseExpire = 3600;
int randomExpire = new Random().nextInt(300);
redisTemplate.opsForValue().set(key, value, baseExpire + randomExpire, TimeUnit.SECONDS);
方案二:核心数据永不过期

对“几乎不变化的核心数据”(如城市列表、商品分类、配置信息),不设置过期时间,避免过期失效问题。若需更新数据,通过“异步线程”主动删除旧缓存并写入新数据(如数据库更新后,发送 MQ 消息触发缓存更新)。

方案三:热点数据缓存预热

在业务高峰到来前(如电商大促、直播带货开始前),通过脚本或后台任务提前将“预知的热点数据”(如热门商品、活动页面数据)加载到缓存中,并设置合理的过期时间(确保高峰期间不过期),避免高峰时缓存 miss。

2. 针对“Redis 整体不可用”的解决方案

方案一:Redis 高可用架构

通过“主从复制+哨兵”或“Redis Cluster 集群”保证 Redis 服务的可用性,避免单点故障:

  • 主从复制:主节点负责写操作,从节点负责读操作,主节点故障时从节点可切换为主节点;
  • 哨兵(Sentinel):实时监控主从节点健康状态,主节点宕机时自动完成故障转移;
  • Redis Cluster:将数据分片存储在多个节点,单个节点故障不影响整体服务,且支持水平扩容。
方案二:多机房容灾

对核心业务,采用“同城双活+异地灾备”架构:

  • 同城双活:同一城市部署两个机房的 Redis 集群,数据实时同步,单个机房故障时,流量可切换到另一个机房;
  • 异地灾备:不同城市部署灾备集群,定时同步数据(如每 5 分钟),极端情况下(如主城市机房被毁)可从灾备集群恢复服务。
方案三:服务兜底措施

即使 Redis 不可用,也要通过“服务降级、限流、熔断”避免数据库和业务服务被打垮:

  • 降级:缓存失效时,返回“默认数据”(如商品默认库存、历史推荐列表)或“服务暂时不可用”提示,减少数据库请求;
  • 限流:通过网关(如 Sentinel、Gateway)限制单位时间内的请求量(如每秒最多 1000 次请求到数据库),避免数据库过载;
  • 熔断:当数据库请求错误率超过阈值(如 50%)时,暂时熔断数据库访问(如 30 秒内不允许请求数据库),避免数据库雪上加霜,熔断后返回降级数据。

总结

缓存三大问题的本质都是“缓存失效导致请求回源数据库”,解决方案的核心思路可归纳为三类:

  1. 限流:通过锁、中间件控制访问数据库的请求数(如击穿的互斥锁、雪崩的限流);
  2. 拦截:在请求到达数据库前过滤无效流量(如穿透的参数校验、布隆过滤器);
  3. 高可用:保证缓存服务稳定+数据不集中失效(如雪崩的高可用架构、过期时间随机化)。

实际业务中,需结合“数据一致性要求”“并发量”“业务场景”选择合适方案(如金融场景优先互斥锁,直播场景优先逻辑过期),必要时可组合多种方案(如布隆过滤器+缓存空值应对穿透),最大化保障系统稳定性。

http://www.dtcms.com/a/399579.html

相关文章:

  • 东营外贸型网站设计怎么做点图片链接网站
  • 网站是怎么做wordpress. 说说样式
  • 2025年最新开源Nano Banana Plus商业化系统本地搭建教程
  • Redis 缓存三大坑:击穿、穿透、雪崩的解析与解决
  • 周口网站关键词优化制作静态动漫网站模板
  • 网站建设制作价格低分类信息专做宠物的网站
  • 【Linux网络】Socket编程:UDP网络编程实现Echo Server
  • 15.Linux 逻辑卷管理、交换空间管理及系统启动管理
  • DeepSeek-V3.1-Terminus深度解析:语言一致性、Agent能力增强,“终极版本“来了?
  • 常州商城网站制作公司2013影响网站百度搜索排名关键因素统计
  • 西安网站优化体验青白江区城乡和建设局网站
  • 社区医疗服务系统的设计与实现
  • 内存屏障与设备内存属性完全指南
  • 基于verilog的轮询调度器
  • 网站权重排名百度网盟如何选择网站
  • 邯郸营销网站建设公司哪家好安徽建设厅考勤网站
  • Spring 框架详细入门知识点
  • Shell常用快捷键和常用文件操作命令
  • 优秀设计网站大全电子商务网站开发环境示范
  • 在随钻测量的演进史中,陀螺为什么是关键角色?
  • seo网站排名全选网站建设工作情况总结
  • 在 Windows GPU 机器上运行 Linux CUDA
  • Python基础总结
  • 二手车网站开发PPT国内专业网站制作
  • 事业单位可以建设网站吗深圳网站设计哪好
  • 做房产推广那个网站好重庆装修价格明细表
  • 工业产品设计网站推荐制作网站首页psd
  • 谷歌seo网站推广怎么做手机网站域名哪里注册时间
  • 记一次添加.h和.cpp后,编译时显示无法解析的外部符号问题
  • 宝安网站制作哪里好长阳网站建设