布隆过滤器是拦截恶意爬虫导致缓存穿透最有效、最轻量的手段——它不依赖空值缓存,不增加redis内存负担,且在请求到达缓存层前就能快速拒绝99%以上的非法id或url。

布隆过滤器是拦截恶意爬虫导致的缓存穿透最有效、最轻量的手段——它不依赖空值缓存,不增加Redis内存负担,且在请求到达缓存层之前就能快速拒绝99%以上的非法ID或URL。
为什么布隆过滤器比空值缓存更适合防爬虫
爬虫常构造大量随机、不存在的 key(如 user:1234567890、goods:-999999),空值缓存会为每个无效 key 写入一条 null,内存随攻击规模线性增长;而布隆过滤器只维护一个固定大小的位数组,1000万有效 ID 占用不到 2MB 内存,且对任意无效 key 的判断耗时稳定在微秒级。
- 空值缓存无法防御“过期窗口穿透”:比如设了 5 分钟 TTL,爬虫每 6 分钟换一批新 key,照样打穿数据库
- 布隆过滤器无 TTL 概念,只要初始化完成,所有查询都是无状态、无过期、无写放大
- 误判率(fpp)可控:设为
0.01时,100 个不存在的 key 最多误判 1 个;实际生产中常用0.001,即千分之一
RedisBloom 模块的初始化与加载时机
直接在 Redis 中启用 bf.add 是错的——布隆过滤器必须在业务数据写入时同步构建,不能等爬虫来了再补。推荐在服务启动阶段或定时任务中批量加载有效 key:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 从数据库全量导出主键(如
SELECT id FROM user WHERE status = 1),用bf.madd批量插入 RedisBloom 过滤器 - 增量同步靠监听 binlog(如用 Canal)或业务层双写:每次新增/启用用户,调用
bf.add user_filter 123456 - 避免用
bf.reserve手动指定capacity和error_rate:容量估小会导致频繁扩容(性能抖动),估大会浪费内存;建议用bf.reserve user_filter 0.001 10000000(支持 1000 万 key,误判率 0.1%)
Java 侧如何安全调用布隆过滤器判断
不要在每次请求里都执行 bf.exists ——网络往返开销大,且 RedisBloom 命令本身不支持 pipeline 批量判断。正确做法是把布隆过滤器下沉到应用本地缓存一层:
- 用
RedissonClient.getBloomFilter("user_filter")获取分布式布隆过滤器实例,它内部已封装连接池和重试逻辑 - 首次调用
isExists(userId)时触发远程检查,并将结果(true/false)缓存在本地ConcurrentHashMap或 Caffeine 中,TTL 设为 1 小时(避免本地缓存长期不准) - 若
bf.exists返回false,直接返回 404,**绝不查数据库**;若返回true,才走正常缓存逻辑(查 Redis → 查 DB → 回填) - 特别注意:布隆过滤器只能确认“不存在”,不能确认“存在”。所以
true结果只是放行,不是最终答案
爬虫绕过布隆过滤器的常见方式及应对
真正危险的不是随机 ID 爬虫,而是能动态发现有效 key 空间的“智能爬虫”——比如先试探 user:1~user:100,发现规律后批量生成 user:1000001~user:1000100。这时单靠布隆过滤器不够:
- 在 Nginx 层加
limit_req,按 IP 限制/user/{id}接口的 QPS(如 5r/s),对高频试探行为硬限流 - 业务接口增加轻量级签名校验,如要求请求头带
X-Sign: md5(userId + salt),salt 每小时轮换,让爬虫无法批量伪造 - 布隆过滤器本身要定期重建(如每天凌晨),防止因数据删除导致“漏判”(即本该存在的 key 被误判为不存在);重建过程需双写过渡,避免中间窗口
布隆过滤器不是银弹——它的价值在于把“无效请求拦截”这件事从数据库前移到 Redis 前,甚至推到网关前;但一旦攻击者开始模拟合法行为、伪造签名、混用代理 IP,就得靠多层协同防御。最容易被忽略的一点是:布隆过滤器的初始化失败或超时,必须有降级开关(如配置中心控制是否跳过 bf.exists 检查),否则服务可能大面积 500。










