wiredtiger缓存对缓存击穿无效,因其是mongodb底层存储引擎的内存页缓存,不感知应用层key生命周期,也不提供加锁、空值写入等防护机制,仅加速磁盘读取。

WiredTiger 缓存和缓存击穿没有直接关系。 你不能靠调大 wiredTiger.cacheSizeGB 来预防缓存击穿——它管的是 MongoDB 存储引擎的内存页缓存,不是应用层的 Redis 或 Laravel cache 那种“缓存”,更不参与 key 是否过期、是否加锁等逻辑。
为什么 WiredTiger 缓存对缓存击穿无效?
缓存击穿是应用架构层的问题:某个热点 key(比如商品 ID "prod_123")在 Redis / Laravel cache 中过期瞬间,大量并发请求同时穿透到数据库。而 WiredTiger 是 MongoDB 底层存储引擎的内部缓存,它只加速对已存在数据的磁盘读取,不感知上层业务的 key 生命周期,也不拦截或协调“多个请求查同一个失效 key”这种场景。
- 即使你把
wiredTiger.cacheSizeGB设为 32GB,当 1000 个请求同时查prod_123且该 key 在应用缓存中为空时,它们仍会全部打到 MongoDB —— WiredTiger 只是让这 1000 次查询在 MongoDB 内部更快一点,但无法减少查询次数 - WiredTiger 不提供分布式锁、互斥机制、空值写入、布隆过滤等任何缓存击穿防护能力
- 它的配置影响的是单个 mongod 实例的内存压力和 IO 效率,和“应用是否用错缓存策略”完全不在一个抽象层级
真正该在分片集群里防击穿的地方是应用层
分片集群本身(shard + mongos + config server)不自带缓存击穿防护。你需要在访问 MongoDB 的上层代码中实现,常见组合如下:
- 对高并发读的集合(如
shop.product),在应用中使用Cache::lock()(Laravel)或redis.setex("lock:prod_123", 30, "1")手动加锁,保证只有一个请求去查 DB 并回填缓存 - 对确定存在的热点 key(如秒杀商品),设置永不过期(
Cache::forever()),配合后台任务异步更新,避免过期点集中 - 在
mongos前加一层布隆过滤器(如用 Redis Bitmap 或专用服务),提前拦截prod_999999这类明显不存在的 ID 请求,防止其进入分片路由环节 - 利用 MongoDB 分片键的局部性,在应用中对同一分片键的请求做客户端限流(例如基于
productId的哈希限流),避免单个 shard 瞬间被打爆
WiredTiger 缓存容量该什么时候调?
它只在以下真实场景中有意义:
- 你的分片集群中某个
shard节点频繁出现Page faults(通过db.serverStatus().extra_info.page_faults查看),说明 WiredTiger 缓存太小,热数据不断被换出,导致磁盘 IO 上升 - 你观察到
mongostat中netIn很低但qr(queue read)持续 > 5,且conn接近上限,可能是 WiredTiger 缓存不足加剧了锁竞争 - 你用了大量
$lookup或聚合管道,且涉及的集合未被充分缓存,此时增大wiredTiger.cacheSizeGB可能降低 pipeline 执行延迟
注意:wiredTiger.cacheSizeGB 默认是物理内存的 50%(上限 10GB),在分片环境下建议设为单机内存的 60%~70%,但绝不能超过物理内存的 80%,否则会触发系统 OOM killer。
真正容易被忽略的点是:很多人以为“数据库层缓存强了,应用层就可以偷懒”,结果在 mongos 路由层没做任何 key 分流或预校验,又把 cacheSizeGB 调得过高,反而挤占了应用进程可用内存,导致 PHP-FPM 或 Node.js 实例因 GC 频繁而响应变慢——这时候问题看起来像“缓存击穿”,实际是资源错配。











