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

【Redis】如何处理缓存穿透、击穿、雪崩

Redis 缓存穿透、击穿和雪崩是高并发场景下的典型问题,以下是详细解决方案和最佳实践:


一、缓存穿透(Cache Penetration)

问题:恶意请求不存在的数据(如不存在的ID),绕过缓存直接访问数据库。

解决方案:
  1. 布隆过滤器(Bloom Filter)

    • 前置过滤器拦截非法请求,存储所有合法键的指纹。
    • 实现示例
      # 使用RedisBloom模块(需安装)
      # 创建布隆过滤器,预期容量100万,错误率1%
      BF.RESERVE my_filter 0.01 1000000
      # 将合法ID加入过滤器
      BF.ADD my_filter valid_id_1
      # 查询前先检查过滤器
      if BF.EXISTS my_filter request_id:
          # 查缓存或数据库
      else:
          return None
      
  2. 缓存空对象(Null Caching)

    • 对查询结果为空的请求,缓存短时效的空值。
    • 实现示例
      Object value = redis.get(key);
      if (value == null) {
          value = db.query(key);
          if (value == null) {
              // 缓存空值,设置较短过期时间(如30秒)
              redis.setex(key, 30, "NULL");
          } else {
              redis.setex(key, 3600, value);
          }
      } else if ("NULL".equals(value)) {
          return null; // 避免数据库查询
      }
      
最佳实践:
  • 结合布隆过滤器与空对象缓存:先用过滤器拦截绝对不存在的数据,对可能存在的Key缓存空值。
  • 定期清理空值缓存,避免内存浪费。

二、缓存击穿(Cache Breakdown)

问题:热点Key突然过期,大量并发请求直接击穿到数据库。

解决方案:
  1. 互斥锁(Mutex Lock)

    • 使用分布式锁控制单线程重建缓存。
    • 实现示例(Redis分布式锁)
      def get_data(key):
          data = redis.get(key)
          if data is None:
              # 尝试获取锁(SETNX + 超时时间)
              if redis.setnx("lock:" + key, 1, ex=10):
                  try:
                      data = db.query(key)
                      redis.setex(key, 3600, data)
                  finally:
                      redis.delete("lock:" + key)
              else:
                  # 等待并重试或返回默认值
                  time.sleep(0.1)
                  return get_data(key)
          return data
      
  2. 逻辑过期(Logical Expiration)

    • 缓存永不过期,但存储逻辑过期时间,异步更新。
    • 数据结构示例
      {
          "value": "真实数据",
          "expire": 1715000000 // 逻辑过期时间戳
      }
      
    • 后台线程检测过期时间并主动更新。
最佳实践:
  • 对热点Key启用永不过期策略,结合异步更新(如定时任务或消息队列触发更新)。
  • 使用Redisson等成熟库实现分布式锁,避免手写锁逻辑出错。

三、缓存雪崩(Cache Avalanche)

问题:大量缓存Key同时过期,导致数据库负载骤增。

解决方案:
  1. 随机过期时间

    • 基础过期时间 + 随机偏移量(如30分钟±随机600秒)。
    • 示例代码
      int baseExpire = 1800; // 30分钟
      int randomExpire = ThreadLocalRandom.current().nextInt(-600, 600);
      redis.setex(key, baseExpire + randomExpire, value);
      
  2. 多级缓存架构

    • 本地缓存(如Caffeine) + Redis + 数据库。
    • 流程
      1. 先查本地缓存
      2. 本地未命中则查Redis
      3. Redis未命中则查数据库并回填
  3. 服务熔断与降级

    • 使用Hystrix或Sentinel在数据库压力过大时触发降级策略(如返回默认值)。
最佳实践:
  • 关键数据设置分层过期时间(如核心数据永不过期+异步刷新)。
  • 启用Redis Cluster或Sentinel实现高可用,避免单点故障。

综合防御策略

  1. 监控与预热
    • 实时监控缓存命中率,热点数据提前预热。
    • 使用Redis的SLOWLOG分析慢查询。
  2. 压测验证
    • 模拟极端场景(如缓存宕机),验证降级策略是否生效。
  3. 代码示例(综合方案)
    public Object getData(String key) {
        // 1. 布隆过滤器拦截
        if (!bloomFilter.mightContain(key)) {
            return null;
        }
        // 2. 查缓存
        Object value = redis.get(key);
        if (value != null) {
            return "NULL".equals(value) ? null : value;
        }
        // 3. 获取分布式锁重建缓存
        RLock lock = redisson.getLock("lock:" + key);
        try {
            lock.lock();
            // 二次检查缓存
            value = redis.get(key);
            if (value == null) {
                value = db.query(key);
                redis.setex(key, 300 + random.nextInt(60), value == null ? "NULL" : value);
            }
            return value;
        } finally {
            lock.unlock();
        }
    }
    

总结

  • 穿透:布隆过滤器 + 空值缓存。
  • 击穿:分布式锁 + 逻辑过期。
  • 雪崩:随机TTL + 多级缓存。
  • 所有方案需结合业务场景调整参数(如过期时间、锁超时时间),并通过压测验证可靠性。
http://www.dtcms.com/a/108494.html

相关文章:

  • 在 Blazor 中使用 Chart.js 快速创建数据可视化图表
  • 大模型生成浏览器端的初级俄罗斯方块
  • 美团民宿 mtgsig 小程序 mtgsig1.2 分析
  • Unity中的C#脚本中文字符无法正常显示问题
  • mysql-创建和删除索引的两种方式
  • 针对 MySQL 数据库中 主键/唯一约束的更新方法 和 ON DUPLICATE KEY UPDATE 语法的详细说明及示例,并以表格总结
  • YOLOv11区域检测
  • Element Plus 常用组件
  • 超越人工智能驱动的网络攻击:现代防御者的策略
  • 电子电气架构 --- 智能座舱域控设计
  • Flutter 音视频播放器与弹幕系统开发实践
  • RK3588使用笔记:USB转232、485、422模块驱动适配
  • [蓝桥杯 2024 省 A] 训练士兵
  • 虚拟试衣间-云尚衣橱小程序-衣橱管理实现
  • 算法:二进制求和
  • 从零构建大语言模型全栈开发指南:第四部分:工程实践与部署-4.3.3低代码开发:快速构建行业应用(电商推荐与金融风控案例)
  • Python 驱动周立功200U收发报文
  • 国产系统服务器识别不到SATA盘
  • STM32学习笔记之RCC模块(实操篇)
  • 横扫SQL面试——中位数问题
  • git tag
  • Linux 中CIFS和NFS使用说明
  • MCP学习总结
  • 基于RapidIO接口的DSP+GPU工业AI实时计算解决方案
  • ‘conda‘ 不是内部或外部命令,也不是可运行的程序或批处理文件
  • 【YOLOv8】YOLOv8改进系列(12)----替换主干网络之StarNet
  • 系统安全——文件监控-FileMonitor
  • 数据在内存中存储(C语言)
  • 杂篇-行业分类一二-2(通、专用设备制造,汽车制造)
  • (done) MIT6.824 Lecture 02 - RPC and Threads