是,索引页确被WiredTiger缓存加载,混在page images中;bytes currently in the cache接近上限且page images占比超70%时,表明数据与索引共同挤占缓存。

索引是否真在吃内存:先确认wiredTiger.cache里有没有它
WiredTiger 的缓存(bytes currently in the cache)里确实会加载索引页,但不是所有索引都会常驻——只有被频繁访问的索引页才会被保留在缓存中。如果 db.serverStatus().wiredTiger.cache["bytes currently in the cache"] 接近 cacheSizeGB 配置上限,且 bytes belonging to page images in the cache 占比异常高(比如 >70%),才说明数据+索引共同挤占了缓存空间。
关键点在于:索引本身不“单独报内存占用”,它混在 page images 里;你无法直接查“某个索引占了多少 MB”,只能间接判断。
- 执行
db.collection.stats(),看totalSize和indexSize比值。若indexSize>dataSize× 2,说明索引可能过度冗余 - 用
db.collection.getIndexes()检查是否有重复字段组合的索引(如{a:1}和{a:1, b:1}并存) - 观察
db.currentOp({secs_running: {$gt: 5}})中是否大量操作命中了某个大索引(如indexName字段反复出现)
哪些索引最容易把cache撑爆
不是所有索引对内存压力一样。以下几类在 WiredTiger 缓存中“占地特别狠”:
-
text索引:每个词项都生成独立索引条目,碎片多、页分散,缓存效率低 - 复合索引中带
array字段(如{tags: 1, ts: -1}):一个文档可能展开成多个索引条目,页数量指数级增长 - 未压缩或低压缩率的字段(如长字符串、Base64 内容):WiredTiger 对这类数据压缩效果差,缓存页体积大
- 只写不读的索引:虽然不被查询使用,但写入时仍要更新索引页,这些页可能长期滞留在 cache 中(WiredTiger 不主动踢出冷页)
怎么验证某个索引是不是罪魁祸首
不能靠删了再看——生产环境禁用这种操作。稳妥办法是“隔离观察”:
- 临时禁用疑似索引:用
db.collection.dropIndex("name_1_ts_-1")(注意:这会触发compact式重建,期间锁表) - 改用
hidden: true隐藏(MongoDB 4.4+):db.runCommand({collMod: "collection", index: {name: "name_1_ts_-1", hidden: true}}),索引仍在磁盘、不参与查询、也不再被写入更新,缓存压力会逐步下降 - 对比
db.serverStatus().wiredTiger.cache中bytes currently in the cache和pages evicted by application threads的变化趋势:若后者明显上升,说明原来有大量无效索引页被驱逐,证实该索引确实在占位
别忽略索引构建阶段的瞬时内存峰值
建索引本身就会吃光 cache——尤其是后台建大集合索引时,WiredTiger 会为索引页预分配大量缓存空间,导致 bytes currently in the cache 短时冲到 95%+,甚至触发脏数据阻塞(Cache Dirty > 20%)。
这不是常态问题,但容易误判为“索引常年占满内存”:
- 检查
db.currentOp({secs_running: {$gt: 0}, "command.createIndexes": {$exists: true}})是否有正在运行的建索引任务 - 用
mongostat --host xxx --port 27017 -o "insert,query,delete,update,dirty,used,netIn,conn"观察dirty和used列是否同步飙升 - 建索引完成后,cache 不会立刻回落,需等待 eviction 线程慢慢清理;若长期不降,说明该索引已被热数据“固化”进缓存
真正难处理的,是那些既没被查询使用、又没被写入淘汰、却始终赖在 cache 里的索引页——它们不会报错,也不会告警,只在你扩容到极限时突然让你意识到:原来一半 cache 早被自己建的索引悄悄占掉了。











