缓存穿透、缓存击穿、缓存雪崩的区别与解决方案

1. 缓存穿透(Cache Penetration)

  • 定义:请求访问数据库中不存在的数据,导致缓存层无法命中,流量直接穿透到数据库。
  • 常见场景:恶意攻击(如频繁请求不存在的用户ID)、参数非法(如负数ID)。
  • 解决方案

    • 布隆过滤器(Bloom Filter)
      在缓存层前加一层布隆过滤器,预存所有合法Key的哈希值。请求到达时,先检查布隆过滤器:

      • 若不存在,直接拦截请求,返回空。
      • 若存在,再查询缓存或数据库。
    • 缓存空值(Cache Null)
      对查询结果为空的Key,缓存一个短期的占位符(如NULL:5min),避免重复穿透。
    • 参数校验
      拦截非法请求(如非数字ID、超出业务范围的参数),减少无效查询。

2. 缓存击穿(Cache Breakdown)

  • 定义热点Key突然失效时,大量并发请求直接冲击数据库,导致数据库瞬时压力激增。
  • 常见场景:促销活动中的热门商品信息、突发新闻。
  • 解决方案

    • 互斥锁(Mutex Lock)
      当缓存失效时,通过分布式锁(如Redis的SETNX)让一个线程重建缓存,其他线程等待锁释放后重试。

      String data = redis.get(key);
      if (data == null) {
          if (redis.setnx(lockKey, "1", 3, TimeUnit.SECONDS)) {
              data = db.queryData();
              redis.set(key, data, 30, TimeUnit.MINUTES);
              redis.delete(lockKey);
          } else {
              Thread.sleep(100); // 短暂等待后重试
              return getData(key);
          }
      }
    • 永不过期(Logical Expiration)
      对热点Key设置物理永不过期,通过后台异步线程定期更新缓存,保证数据新鲜度。
    • 熔断降级
      在缓存失效期间,启用降级策略(如返回默认值或静态页面),保护数据库。

3. 缓存雪崩(Cache Avalanche)

  • 定义:大量缓存Key在同一时间过期,导致请求集中访问数据库,引发系统崩溃。
  • 常见场景:批量缓存初始化后同时过期、定时任务集中刷新缓存。
  • 解决方案

    • 随机过期时间
      在基础过期时间上增加随机值(如基础时间 + random(0~300s)),分散Key失效时间。
    • 多级缓存(Multi-Level Cache)
      结合本地缓存(如Caffeine)和分布式缓存(如Redis)。当Redis失效时,本地缓存仍可短暂兜底。
    • 服务熔断与限流
      使用Sentinel或Hystrix在缓存失效期间限制数据库访问的并发量,避免过载。

Redis大Key与大Value问题及解决方案

1. 大Key(Big Key)

  • 定义:单个Key的Value大小超过1MB,或包含大量元素(如Hash/List有10万条记录)。
  • 风险

    • 阻塞主线程:大Key的读写操作耗时高,影响Redis响应速度。
    • 内存不均:导致集群中某些节点内存占用过高,触发OOM。
  • 解决方案

    • 拆分Key

      • Hash分片:将大Hash拆分为多个小Hash,如user:1000:infouser:1000:info:1user:1000:info:2
      • List分页:按范围分片存储(如list:1000:0-1000list:1000:1001-2000)。
    • 异步删除
      使用UNLINK命令代替DEL,非阻塞删除大Key。
    • 压缩存储
      对JSON等文本数据使用压缩算法(如GZIP),减少内存占用。

2. 大Value(Big Value)

  • 定义:单个Value的大小超过10MB
  • 风险

    • 网络延迟:传输大Value消耗带宽,影响其他请求的响应时间。
    • 持久化阻塞:RDB/AOF时大Value可能导致持久化延迟。
  • 解决方案

    • 优化数据结构

      • 使用更紧凑的结构(如用HyperLogLog替代Set统计UV)。
      • 避免存储冗余数据(如仅存ID而非完整对象)。
    • 分片存储
      将大Value拆分为多个小Key,客户端合并处理。
    • 外部存储
      将大Value存储到文件系统(如HDFS),Redis中仅存访问路径。

实际案例与监控手段

案例1:缓存穿透防御

在金融平台的风控系统中,攻击者频繁请求不存在的用户ID,导致数据库负载飙升。通过引入布隆过滤器(预存所有有效用户ID哈希)和空值缓存(设置5分钟过期),穿透请求减少99%。

案例2:大Key优化

某电商平台的商品评论列表(List类型)包含50万条记录,导致LRANGE操作耗时超过1秒。通过分片存储(每片1000条)和按需加载(仅查询前N页),响应时间降至50ms。

监控与工具

  • Redis慢查询日志:通过slowlog get命令分析耗时操作,定位大Key。
  • 内存分析工具:使用redis-rdb-tools分析RDB文件,识别内存占用高的Key。
  • 告警配置:对Key大小、内存使用率设置阈值告警(如Prometheus+Alertmanager)。

总结

  • 缓存问题

    • 穿透 → 布隆过滤器 + 空值缓存。
    • 击穿 → 互斥锁 + 永不过期策略。
    • 雪崩 → 随机过期 + 多级缓存。
  • 大Key/Value

    • 拆分、压缩、异步删除。
  • 核心原则

    • 预防优于修复:在业务设计阶段规避大Key。
    • 监控常态化:通过工具及时发现并优化异常Key。

今夜有点儿凉
40 声望3 粉丝

今夜有点儿凉,乌云遮住了月亮。