thinkphp中需用cache('key', '__null__', ['expire'=>300])写空值缓存,读取后显式判断是否等于'__null__';布隆过滤器须前置校验,通过$redis->rawcommand调用redis原生命令,且容量与误差率需压测调优。

ThinkPHP里怎么安全写空值缓存
ThinkPHP 的 Cache::get() 和 cache() 会把「缓存里存了 null」和「根本没这个 key」都返回 null,你无法区分——这就导致空值缓存形同虚设,下一次请求照样穿透。
必须绕过高层封装,直连底层驱动写入明确标记值:
- 用
cache('user:999999', '__NULL__', ['expire' => 300]),别用Cache::remember(),它压根不处理空结果 - 标记值别用
null、''或false,推荐字符串'__NULL__'或'NULL',避免反序列化歧义 - 过期时间设短,比如
300秒(5 分钟),太长会导致真实数据上线后用户等很久才刷出来
为什么不能只靠 Cache::get() 判断是否存在
因为 Cache::get('xxx') 返回 null 时,你根本不知道是「key 不存在」还是「key 存的是 null」——ThinkPHP 的缓存门面做了统一兜底,抹掉了这个关键语义。
真实场景中,这会让空值缓存彻底失效:
- 第一次查
user:999999,DB 没数据,你写了cache('user:999999', '__NULL__', 300) - 第二次再调
Cache::get('user:999999'),它还是返回null,你误以为「没缓存」,又去查 DB - 结果空值缓存白写了,穿透照旧
所以读取后必须显式判断:if ($val === '__NULL__') { return null; }
布隆过滤器在 ThinkPHP 里怎么配才不翻车
别自己手写 PHP 版布隆过滤器——进程模型下每次请求重载 filter,内存暴涨,还容易因并发写入错乱。Redis 7.0+ 原生支持 BF.RESERVE、BF.ADD、BF.EXISTS,这才是正解。
ThinkPHP 中只需透传命令:
- 初始化时用
$redis->rawCommand('BF.RESERVE', 'user_bf', 0.01, 1000000)创建过滤器 - 写入合法 ID 时执行
$redis->rawCommand('BF.ADD', 'user_bf', $userId) - 查询前先
$exists = $redis->rawCommand('BF.EXISTS', 'user_bf', $userId) === '1',为0就直接返回 404 - 注意:布隆过滤器只防「已知非法 ID」,对随机构造的 ID(如
user:1234567890)仍有误判率(典型 0.1%),所以空值缓存仍是必要兜底
空值缓存 + 布隆过滤器,哪个该先执行
顺序错了就等于没防——必须「布隆过滤器前置校验」,而不是先查缓存再想布隆。
正确链路是:请求进来 → 先 BF.EXISTS → 不存在就 404 → 存在才查 Redis → Redis 没命中才查 DB → DB 无结果就写空值缓存。
如果把布隆过滤器放在缓存之后,那所有恶意请求已经打到 Redis 了,QPS 高时反而把 Redis 自己拖慢;而前置之后,99% 的无效请求在第一关就被拦住,Redis 和 DB 完全无感。
最容易被忽略的一点:布隆过滤器的容量和误差率要在上线前压测调优,BF.RESERVE user_bf 0.001 1000000 和 0.01 100000 内存占用差十倍,但误判率也差十倍——别抄网上随手写的参数。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











