totalIndexSize 查 db.stats() 的字段,表示磁盘上所有索引总大小(字节),非实时内存占用;其值不等于驻留内存量,因 MongoDB 采用按需加载机制,实际进内存的仅热点索引页。

查 totalIndexSize 到底看哪个命令
直接连上 MongoDB,运行 db.stats() 就能看到 totalIndexSize 字段,单位是字节。注意这不是实时内存占用,而是所有索引在磁盘上的总大小(BSON 序列化后、含 padding 的大小)。它和实际驻留 RAM 的量不是 1:1 关系——MongoDB 用的是内存映射文件(mmapv1)或 WiredTiger 的 cache 机制,真正进内存的只是最近/最热的那部分。
更准的参考是 db.serverStatus().metrics.memory(WiredTiger 场景下看 wiredTiger.cache 下的 bytes currently in the cache),但这个值反映的是整个 cache 使用量,不单是索引。
-
totalIndexSize是“索引体积上限”,不是“当前内存开销” - WiredTiger 默认只把活跃数据页放进 cache,默认 cache 大小是 50% 物理 RAM(上限 1GB),可调
wiredTigerCacheSizeGB - mmapv1 已弃用,但若还在用,索引会随访问逐步 mmap 进虚拟内存,RSS 增长不线性,难精确估算
索引进内存的三个关键条件
即使 totalIndexSize 小于 RAM,也不代表索引全在内存里——MongoDB 不预加载,只按需载入。是否真进内存,取决于这三个事实:
- 查询是否命中该索引(
explain("executionStats")看indexName和nReturned是否匹配) - 索引键字段是否被频繁读取(比如
{status: 1, createdAt: -1}被大量 range 查询,对应 B-tree 节点就容易留在 cache) - 是否有足够 cache 压力:当新数据/索引页进来,WiredTiger 会按 LRU-like 策略淘汰冷页;如果写入密集,索引页可能被挤出 cache
一个典型反例:totalIndexSize 是 8GB,RAM 有 16GB,但某复合索引只在凌晨批处理时用一次,白天几乎不访问——它大概率不在内存里。
为什么 totalIndexSize 突然涨得比数据还快
索引体积膨胀常见于以下操作,和 RAM 占用间接相关(因为更大索引 = 更多潜在 cache 压力):
- 对数组字段建索引(如
{tags: 1}),每个数组元素生成独立索引条目,totalIndexSize可能指数级增长 - 文本索引(
text)默认为每个词干建倒排项,短文本少,长文章多,且不压缩词干重复 - 未清理的 TTL 索引:过期文档删了,但索引条目不会立刻回收,要等后台线程合并,期间仍计入
totalIndexSize - 重复建索引(比如应用重启反复执行
createIndex),旧索引没删,新索引又加,totalIndexSize叠加
查冗余索引用 db.collection.getIndexes(),删掉不用的再看 totalIndexSize 是否回落。
评估 RAM 是否够用的实操检查点
别只盯 totalIndexSize 数字,重点看这几个指标是否告警:
- WiredTiger cache 使用率持续 >95%(
serverStatus.wiredTiger.cache."bytes currently in the cache" / serverStatus.wiredTiger.cache."maximum bytes configured") - page eviction rate 高(
serverStatus.wiredTiger.cache."pages evicted by application threads"每秒 >100) - 查询慢日志里频繁出现
IXSCAN但executionTimeMillis波动大,尤其负载高时突增——说明索引页反复进出 cache -
mongostat显示faults(页错误)持续 >5/s,意味着频繁缺页,磁盘 IO 在补位
这时候加 RAM 不一定解决问题,先确认是不是某个低效索引把 cache 打穿了——比如一个没用的 {_id: 1, status: 1, type: 1, category: 1, ...} 全字段索引,体积大、命中率低,删掉往往比加内存更有效。










