redis无内置拦截器,实际是业务层用bf.exists或get空值key做前置判断:bf.exists需加载redisbloom模块并同步注册合法key,返回0即拒接;空值缓存须用原子set+ex命令写empty:key并设短过期,读命中则直接返回null。

Redis 本身没有可插拔的拦截器机制,所谓“自定义拦截器”实际是业务层借助 Redis 原子能力构建的前置判断逻辑——不是在 Redis 进程里写插件,而是在应用代码里把 BF.EXISTS 或 GET 空值 key 的判断作为请求第一道闸口。
用 BF.EXISTS 做快速存在性判断(需 RedisBloom)
布隆过滤器是生产中最接近“拦截器”效果的方案,但它依赖 RedisBloom 模块,不是开箱即用:
- 必须提前加载模块:
redis-server --loadmodule /path/to/redisbloom.so,否则执行BF.EXISTS会报ERR unknown command - 所有合法 key 写入 DB 后,必须同步调用
BF.ADD product_bf 12345注册,漏掉一个就可能误判为“不存在” -
BF.EXISTS product_bf 12345返回0表示“一定不存在”,此时直接 return,不查缓存也不查 DB;返回1才继续走正常流程 - 误判率(false positive)无法避免,但 false negative 为零——它只用来“拒绝”,不能用来“确认存在”
用 GET + SET 组合实现空值兜底
不依赖额外模块,适合中小规模场景,但要注意原子性和 key 设计:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 空值 key 必须带统一前缀,比如
empty:product:9999999,避免和业务 key(如product:9999999)冲突 - 写空值必须用原子命令:
SET empty:product:9999999 1 EX 300,不要拆成SET+EXPIRE,否则并发下可能残留永久空值 - 读请求先
GET empty:product:9999999,命中就直接返回 null,不往下走;未命中才查product:9999999 - DB 新增该数据时,必须主动
DEL empty:product:9999999,否则缓存不一致
为什么不能用 SCAN 或 KEYS 实时校验?
有人想动态扫描 Redis 所有 key 来判断请求 key 是否“已知”,这是危险操作:
-
KEYS *会阻塞 Redis 单线程,QPS 稍高就导致整个实例卡死 -
SCAN虽不阻塞,但全量遍历耗 CPU,且结果非实时——刚 SCAN 完,新 key 就被写入或删除 - 无法区分“真不存在”和“刚过期还没被清理”的空状态,没有原子性保证
- 这违背了“写时注册、读时查表”的可靠原则,属于反模式
真正有效的拦截永远建立在“写入时同步注册”基础上,无论是布隆过滤器还是空值 key,漏掉一次写同步,就可能让防御失效。线上环境务必监控 BF.INFO 的 capacity 使用率,以及 empty: 类 key 的内存占比——它们不是临时补丁,而是需要持续维护的数据契约。










