侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

如何设计解决方案应对Redis缓存穿透问题?

2025-12-7 / 0 评论 / 4 阅读

题目

如何设计解决方案应对Redis缓存穿透问题?

信息

  • 类型:问答
  • 难度:⭐⭐

考点

缓存穿透原理,布隆过滤器应用,空对象缓存策略,防御性编程

快速回答

应对Redis缓存穿透的核心方案:

  • 布隆过滤器:前置过滤非法请求
  • 缓存空对象:对不存在的数据设置短时缓存
  • 请求校验:在业务层增加参数合法性检查
  • 热点Key监控:实时监控高频访问的Key
## 解析

一、问题原理与危害

缓存穿透指查询不存在的数据,导致请求直接穿透缓存层到达数据库:

  • 恶意攻击:攻击者伪造大量不存在ID(如负值、超大整数)
  • 业务缺陷:程序BUG生成非法查询Key
  • 危害:大量请求直接冲击数据库,可能引发服务雪崩

二、解决方案与代码示例

1. 布隆过滤器(Bloom Filter)

原理:使用位数组和哈希函数,以极小空间判断元素一定不存在可能存在于集合中

# Python伪代码示例
from pybloom_live import BloomFilter

# 初始化布隆过滤器(预计元素量100万,误判率0.001)
bf = BloomFilter(capacity=1000000, error_rate=0.001)

# 预热数据:将有效ID加入过滤器
for id in valid_id_list:
    bf.add(id)

# 查询拦截
def get_data(key):
    if key not in bf:  # 一定不存在
        return None
    # 继续查询缓存/数据库
    data = redis.get(key)
    if not data:
        data = db.query(key)
        redis.setex(key, 300, data or '')  # 空值缓存
    return data

注意事项

  • 误判率与位数组大小成反比,需根据业务调整
  • 删除数据时需同步更新过滤器(可用Counting Bloom Filter)

2. 空对象缓存(Null Caching)

// Java示例
public String getData(String key) {
    String value = redis.get(key);
    if (value != null) {
        return "".equals(value) ? null : value; // 处理空值
    }

    value = db.query(key);
    if (value == null) {
        // 设置短时空缓存(5分钟)
        redis.setex(key, 300, ""); 
        return null;
    }
    redis.setex(key, 3600, value); // 正常缓存
    return value;
}

关键点

  • 空值缓存时间不宜过长(建议2-5分钟)
  • 需特殊处理空值(如返回特定错误码)

3. 请求校验与监控

  • 参数校验:在Controller层校验ID格式(如正则匹配正整数)
  • 限流熔断:对高频访问Key实施监控,异常时触发限流
  • 日志审计:记录异常查询模式,识别攻击行为

三、最佳实践

  • 分层防御:API网关校验 → 布隆过滤器 → 空值缓存 → 数据库限流
  • 组合策略:布隆过滤器+空值缓存(应对过滤器误判)
  • 动态调整:根据QPS自动延长空值缓存时间

四、常见错误

  • ❌ 永久缓存空值 → 导致脏数据长期存在
  • ❌ 未设置布隆过滤器容量 → 误判率急剧上升
  • ❌ 忽略Key删除同步 → 过滤器与数据库状态不一致

五、扩展知识

  • 布隆过滤器变种
    • Cuckoo Filter:支持删除操作
    • Scalable Bloom Filter:动态扩容版本
  • Redis原生方案:RedisBloom模块(官方扩展)
    BF.ADD myFilter 12345  # 添加元素
    BF.EXISTS myFilter 999 # 检查是否存在
  • 缓存击穿对比:缓存穿透(数据不存在) vs 缓存击穿(热点Key失效)