布隆过滤器前置拦截与空值缓存协同防御缓存穿透:先用BF判断“肯定不存在”拦截非法ID,再对已确认无效ID写入带TTL的'__NULL__'标记,双策略使无效请求零触达数据库。

高并发场景下,大量请求查询数据库中根本不存在的ID,导致缓存层形同虚设、数据库瞬间被打垮——这不是理论风险,而是真实发生的线上事故。你正在用 Laravel 或 ThinkPHP 开发用户中心模块,刚上线就收到 DB CPU 98% 的告警,日志里全是 user:999999、user:1234567890 这类非法ID查询。
布隆过滤器前置拦截:先判“可能不存在”再放行
第一步:确认 Redis 已加载 RedisBloom 模块(【必须提前部署,不可热加载】)。执行 redis-cli INFO modules | grep bloom,输出 BF 且 version ≥ 2.4 才能继续。
第二步:在服务启动时初始化布隆过滤器,预写入全部合法用户ID哈希值。Laravel 中可在 AppServiceProvider@boot 内执行:
$redis = Cache::getStore()->getRedis();<br>$redis->executeRaw(['BF.RESERVE', 'bf:user', '0.01', '1000000']);<br>// 后续用 $redis->executeRaw(['BF.ADD', 'bf:user', 'user:1001']) 批量注入
立即学习“PHP免费学习笔记(深入)”;
第三步:请求入口处强制校验。ThinkPHP 6 的中间件中插入:
$exists = $redis->executeRaw(['BF.EXISTS', 'bf:user', "user:{$userId}"]);<br>if (!$exists) {<br> return json(['code'=>404, 'msg'=>'Not found'], 404);<br>}
注意:布隆过滤器只返回“可能存在”或“肯定不存在”,误判率设为 0.01 是内存与精度的合理折中;若业务要求零误判(如金融类),此方案不适用,需换用精确索引。
空值缓存兜底:对已知无效ID做短时封禁
方法一:绕过框架缓存封装,直连 Redis 驱动写入明确标记值
Laravel 示例:
$redis = Cache::getStore()->getRedis();<br>$redis->setex('user:999999', 300, '__NULL__');
ThinkPHP 6 示例:cache('user:999999', '__NULL__', ['expire' => 300]);
【禁止使用 Cache::remember() 或 cache() 的自动回写逻辑】——这些方法内部会跳过 null 值,导致空值根本写不进缓存。
方法二:读取后统一识别并透出 null
从缓存取值后,必须显式判断:if ($val === '__NULL__') { return null; }
不能用 empty() 或 isset(),因为 PHP 的 empty('')、empty(0)、empty(null) 全部返回 true,无法区分真实空字符串和空值标记。
双策略协同生效的关键时机
① 布隆过滤器判定“可能存在” → 查 Redis 缓存 → 命中则返回,未命中则查 DB
② DB 返回 null → 立即写入 '__NULL__' + 300 秒 TTL → 同时触发布隆过滤器更新(可选)
③ 下次相同 ID 请求 → 布隆过滤器放行 → Redis 返回 '__NULL__' → 业务层转成 null 返回
布隆过滤器负责拦截 99.9% 的随机构造ID,空值缓存只承载已被验证过的无效ID,二者叠加后,无效请求不再触达数据库。



















