Bootstrap

[冲击大厂]一文彻底掌握Redis缓存雪崩,缓存穿透和缓存击穿+八股吊打面试官

一、缓存雪崩

问题本质

大量缓存数据在同一时间过期(失效)时,如果此时有大量的用户请求,都无法在 Redis 中处理,于是全部请求都直接访问数据库,从而导致数据库的压力骤增,严重的会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃

解决方案
  1. 分散过期时间

    • 随机化过期时间:在基础过期时间上增加随机值(如 基础TTL + 随机1~10分钟),避免集中失效。

    • 代码示例

      java

      复制

      int baseTTL = 3600; // 基础过期时间1小时
      int randomTTL = baseTTL + new Random().nextInt(600); // 增加0~10分钟随机值
      redis.set(key, value, randomTTL);
  2. 缓存永不过期 + 异步更新

    • 不依赖TTL,通过后台线程或消息队列异步更新缓存。

    • 实现逻辑

      • 缓存不设过期时间,但记录数据的版本或更新时间戳。

      • 通过定时任务或订阅数据库变更事件(如Binlog)触发缓存更新。

  3. 互斥锁限流

    • 当缓存失效时,使用分布式锁(如Redis的SETNX)控制只有一个线程重建缓存,其他线程等待或返回降级结果。

    • 代码示例

      java

      复制

      String lockKey = "lock:" + key;
      if (redis.set(lockKey, "1", "NX", "EX", 10)) { // 获取锁
          try {
              // 查询数据库并重建缓存
          } finally {
              redis.del(lockKey); // 释放锁
          }
      } else {
          // 等待重试或返回默认值
      }
  4. 集群分流

二、缓存击穿

问题本质
  • 如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮

    • 可以认为缓存击穿是缓存雪崩的一个子集
解决方案
  1. 热点数据永不过期

    • 通过异步线程定期更新缓存(如每5分钟更新一次)。

    • 优化点:结合双缓存策略(主缓存+备份缓存),确保更新期间仍有数据可用。

  2. 互斥锁重建

    • 类似缓存雪崩的锁方案,但需注意锁粒度(应针对具体Key加锁而非全局锁)。

    • 优化点:使用Redis的RedLock算法或本地锁(如Guava的Striped<Lock>)减少锁竞争。

  3. 逻辑过期时间

    • 缓存不依赖物理TTL,而是存储逻辑过期时间字段,由业务代码判断是否需异步更新。

    • 数据结构示例

      json

      复制

      {
          "value": "真实数据",
          "expire_time": 1710000000 // 逻辑过期时间戳
      }

三、缓存穿透

问题本质

当发生缓存雪崩或击穿时,数据库中还是保存了应用要访问的数据,一旦缓存恢复相对应的数据,就可以减轻数据库的压力,而缓存穿透就不一样了。

当用户访问的数据,既不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据,没办法构建缓存数据,来服务后续的请求。那么当有大量这样的请求到来时,数据库的压力骤增,这就是缓存穿透的问题。

解决方案
  1. 请求预校验

    • 参数合法性校验:如ID必须为数字、长度固定、符合业务范围等。

    • 示例代码

      java

      复制

      if (!isValidParam(param)) { // 校验参数格式
          return "非法参数";
      }
  2. 缓存空值(Null Object)

    • 对查询结果为空的Key,缓存一个短TTL的空值(如""NULL)。

    • 注意点:需定期清理空值,避免存储过多无效Key。

  3. 布隆过滤器(Bloom Filter)

    • 前置过滤:在缓存层前加布隆过滤器,拦截不存在的数据请求。

    • 实现步骤

      1. 写入数据时,将Key同步到布隆过滤器。

      2. 查询时,先检查布隆过滤器:

        • 若不存在,直接返回空。

        • 若存在,继续查缓存或数据库。

    • 优化点

      • 使用可扩展布隆过滤器(如RedisBloom模块)应对数据增长。

      • 允许误判率(如1%),通过调整哈希函数数量和位数组大小平衡性能与精度。


四、方案对比与选型建议

场景推荐方案优点缺点
缓存雪崩分散过期时间 + 异步更新简单易实现,资源消耗低需维护异步更新逻辑
缓存击穿(热点数据)逻辑过期时间 + 互斥锁高并发下稳定性强实现复杂度较高
缓存穿透(非法请求)布隆过滤器 + 空值缓存高效拦截无效请求布隆过滤器需维护数据同步

五、实践注意事项

  1. 监控与告警

    • 监控缓存命中率、数据库QPS、Redis内存使用等指标,设置阈值告警。

  2. 降级策略

    • 当缓存系统异常时,启用本地缓存(如Caffeine)或直接返回默认值,避免系统雪崩。

  3. 数据一致性

    • 异步更新缓存时,需处理并发写冲突(如使用版本号或CAS机制)。

  4. 布隆过滤器维护

    • 数据库删除数据时,需同步清理布隆过滤器(可通过延迟双删或订阅删除事件实现)。

通过上述方案组合,可显著提升缓存系统的稳定性和抗压能力。

;