发布于2026-07-18 阅读(0)
扫一扫,手机访问
先看一个现实场景:你的 Lara vel 应用用了 MongoDB 或 Redis 做缓存,用户量一大,突然发现数据库压力飙升,直接被打穿。这大概率就是遭遇了缓存穿透——恶意构造的、数据库里根本不存在的 key,像洪水一样绕过了缓存,直接冲击了底层存储。
那问题来了,怎么防?下面这几种策略,是经过实战检验的,能帮你构建一个坚固的防护体系。
这是最直接、也最常用的一招。思路很简单:既然查不到数据,那就连“查不到”这个结果也缓存起来,只不过给它一个很短的生命周期。这样一来,后续同样的“无效 key”请求,就直接命中缓存,不会再穿透到数据库了。
具体操作上,有几点需要注意:
1. 在数据库查询逻辑返回后,要判断结果是否为空。
2. 如果为空,就调用 Cache::put('key', null, 300),TTL 设置为 300 秒(5 分钟),把“空”这个结果也存进去。
3. 读取缓存时,统一用 Cache::get('key')。即使返回 null,也视为缓存命中,直接返回,不再去查库。
4. 一个关键细节:确保你的 MongoDB 缓存驱动中的 MongoStore::get() 方法,不会因为值为 null 就跳过校验,必须保留原始存储内容。
如果说空值缓存是“事后补救”,那布隆过滤器就是“事前拦截”。它是一种空间效率极高的概率型数据结构,能快速判断一个 key 是否“有可能”存在于数据库中。对于确认不存在的 key,直接拒之门外,连缓存都不用查。
实现起来也不复杂:
1. 通过 Composer 安装扩展:composer require odan/bloom-filter。
2. 创建一个 BloomFilterService 类,初始化一个容量为 1000000、误判率 0.001 的过滤器实例。这个误判率是可控的,通常这组参数就够用了。
3. 关键一步:在数据写入数据库成功后,同步调用 $bloomService->add($id),把合法的 key 都加到过滤器里。
4. 在查询入口处,先执行 $bloomService->has($id) 进行检查。如果返回 false,说明这个 key 肯定不存在,直接返回 404 或空响应,连缓存和数据库查询都省了。
布隆过滤器虽然好用,但在冷启动或某些场景下,可能还没覆盖到所有合法 key。这时候,如果多个并发请求同时查询一个不存在的 key,就会瞬间穿透到数据库,形成压力尖峰。分布式锁就是用来解决这个问题的。
具体做法是:
1. 利用 Lara vel-MongoDB 提供的 MongoLock 组件,通过 acquire('lock:item:9999') 获取指定 key 的分布式锁。
2. 拿到锁之后,先别急着查库,再检查一遍缓存。如果缓存里还是空的,再去执行数据库查询。
3. 如果数据库查询结果依然是空,就执行 Cache::put('item:9999', null, 300),然后释放锁。这样,第一个请求就把“空”结果缓存好了。
4. 那些没拿到锁的请求,等待一个短暂的延迟(比如 100ms)后,重新尝试读取缓存,而不是直接去查库。这就能有效避免并发穿透。
用 MongoDB 做缓存,空值缓存如果一直堆着,也会占用磁盘空间。好在 MongoDB 的 TTL 索引可以自动清理过期文档,让这些空值缓存“寿终正寝”。
这个方案的关键在于:
1. 确认你的 Lara vel-MongoDB 配置中,缓存集合名是 cache,并在 MongoDB 中为该集合的 expires_at 字段建立 TTL 索引。
2. 确保在 MongoStore::put() 方法中,每次写入缓存时,都写入带时间戳的 expires_at 字段,值就是当前时间加上 TTL 秒数。
3. 对于空值缓存,同样写入 expires_at,但它的 TTL 要显著短于正常数据(比如空值 5 分钟,正常数据 1 小时)。
4. 之后,MongoDB 的后台线程就会自动按索引规则清理过期的空值文档,完全不需要应用层操心。
最后,别忘了安全。如果用户可以直接控制缓存键的生成,那前面的所有防护都可能被绕过。所以,必须从源头隔离用户输入,杜绝恶意构造的键。
几条铁律:
1. 绝对禁止把原始的、未经处理的用户输入(比如 URL 参数、表单字段)直接拼接到缓存键里。
2. 对所有键的组成部分,先执行 array_values(),再用 md5(serialize($params)) 进行哈希处理,确保键结构稳定且不可预测。
3. 对于敏感参数,如用户 ID,要加一层哈希脱敏。比如用 hash_hmac('sha256', $userId, config('app.key')) 来替代明文。
4. 最后,在缓存键前缀中加入命名空间标识,比如 'prod:cache:user:'. $hashedId,这样能避免跨环境或跨业务的键冲突。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8