Redis 布隆过滤器如何解决缓存穿透

面试题背景

在高并发系统中,缓存穿透是一个经典且高频的面试考点。所谓缓存穿透,是指客户端请求查询一个数据库中不存在的数据,由于缓存中也没有这条数据,每次请求都会直接打到数据库上。如果这类请求被恶意利用(例如伪造大量不存在的 ID 发起攻击),数据库可能在短时间内承受巨大压力甚至被打垮。

面对这个问题,最常用的解决方案之一就是 Redis + 布隆过滤器(Bloom Filter)。本文将从面试答题的角度,把原理、落地方式和常见追问一次讲清楚。

一、缓存穿透的本质

正常缓存流程是:

  1. 请求先查 Redis 缓存。
  2. 命中则直接返回。
  3. 未命中则查数据库,再把结果写回缓存。

问题出在第 2、3 步之间:如果查询的 key 在数据库中根本不存在,那么缓存永远不会被写入,导致每次请求都穿透到数据库。这与缓存击穿(热点 key 失效)和缓存雪崩(大量 key 同时失效)不同,穿透的关键是“数据本身不存在”。

二、布隆过滤器是什么

布隆过滤器是一种空间效率极高的概率型数据结构,用于判断一个元素“可能存在”或“一定不存在”。

它的核心结构是一个很长的位数组(bit array)和若干个哈希函数

  • 添加元素:用 k 个哈希函数对元素求值,得到 k 个位置,把这些位置的 bit 置为 1。
  • 查询元素:同样计算 k 个位置,只要有一个位置是 0,就说明该元素一定不存在;如果全为 1,则说明元素可能存在

注意这里的关键特性:布隆过滤器只会误判“存在”,不会误判“不存在”。也就是说,它说“不存在”就一定不存在,这就足够用来拦截穿透请求了。

三、如何用它解决缓存穿透

整体思路是:在访问缓存和数据库之前,先用布隆过滤器做一层前置校验。

具体流程如下:

  1. 预热阶段:把数据库中所有合法的 key(例如所有商品 ID、用户 ID)预先加载进布隆过滤器。
  2. 请求到来时
    • 先用布隆过滤器判断该 key 是否存在。
    • 如果判断为“不存在”,直接返回空结果,不查缓存、不查数据库
    • 如果判断为“可能存在”,再走正常的缓存 → 数据库查询流程。
  3. 新增数据时:向数据库写入新数据后,同步把该 key 加入布隆过滤器。

这样一来,那些查询不存在数据的恶意请求会在布隆过滤器这一层被直接拦掉,数据库压力大幅降低。

四、代码示例(伪代码)

public Object query(String key) {
    // 1. 布隆过滤器前置校验
    if (!bloomFilter.mightContain(key)) {
        // 一定不存在,直接返回,避免穿透
        return null;
    }

    // 2. 查缓存
    Object cacheValue = redis.get(key);
    if (cacheValue != null) {
        return cacheValue;
    }

    // 3. 查数据库
    Object dbValue = db.query(key);
    if (dbValue != null) {
        redis.set(key, dbValue, EXPIRE_TIME);
    }
    return dbValue;
}

配合 Redis 时,通常使用 RedisBloom 模块(提供 BF.ADDBF.EXISTS 命令),或者用 Guava 的 BloomFilter 在应用层实现。分布式场景下更推荐 RedisBloom,保证多实例共享同一份过滤数据。

五、关键参数与误判率

布隆过滤器的误判率(false positive rate)由三个因素决定:

  • 位数组长度 m
  • 哈希函数个数 k
  • 预期插入元素数量 n

在面试中常被问到如何估算。给定 n 和期望误判率 p,最优的位数组长度为:

m ≈ -n * ln(p) / (ln2)^2

最优哈希函数个数为:

k = (m / n) * ln2

例如预期插入 100 万个元素、误判率控制在 1%,大约需要 958 万 bit(约 1.14 MB)和 7 个哈希函数。可以看到,布隆过滤器非常节省内存,这也是它适合做前置拦截的原因。

六、优缺点与注意事项

优点:

  • 空间占用极小,查询时间复杂度为 O(k)。
  • 拦截不存在的数据,效果显著。
  • 不存在“漏判不存在”的情况。

缺点与坑点:

  1. 存在误判:判断“存在”时可能是假阳性,导致少量请求仍会穿透,但比例可控。
  2. 不支持删除:标准布隆过滤器无法删除元素,因为置 1 的位可能被多个元素共享。若业务需要删除,可考虑计数布隆过滤器(Counting Bloom Filter)布谷鸟过滤器
  3. 数据同步问题:数据库新增数据后必须同步更新过滤器,否则新数据会被误判为不存在。通常采用异步补偿或定时重建。
  4. 预热成本:初始化时需要把全量合法 key 加载进去,数据量大时要注意耗时。

七、与其他方案的对比

面试中常被追问“还有哪些方案,怎么选”:

  • 缓存空对象:把查询结果为空的 key 也缓存起来(设较短过期时间)。实现简单,但会占用额外内存,且可能被大量随机 key 攻击。
  • 布隆过滤器:内存占用小,能拦截绝大多数无效请求,适合 key 集合相对固定的场景。
  • 接口层限流/参数校验:从源头限制非法请求,通常作为补充手段。

实际生产中,布隆过滤器 + 缓存空对象组合使用是常见做法:过滤器挡住大部分,空对象兜住漏网的假阳性请求。

八、面试答题要点总结

回答这道题时,建议按以下逻辑组织:

  1. 先解释缓存穿透的定义和危害。
  2. 说明布隆过滤器的原理,强调“说不存在就一定不存在”。
  3. 讲清前置校验的完整流程。
  4. 补充误判率、不支持删除、数据同步等关键细节。
  5. 最后对比其他方案,体现方案的取舍能力。

掌握这几点,基本可以完整、有深度地回答“Redis 布隆过滤器如何解决缓存穿透”这道高频面试题。

未经允许不得转载:任鹏个人博客 » Redis 布隆过滤器如何解决缓存穿透

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏