加索引能直接降CPU,因为B-tree跳过大量文档解析和内存排序,使docsExamined从百万级降至几十;需优先为高频高选择性filter字段、sort字段及filter+sort组合建复合索引,且sort字段须置末位。

为什么加索引能直接降CPU?
MongoDB 的 CPU 高,80% 以上来自 docsExamined 过大或 planSummary 显示 COLLSCAN。本质是:没索引时,每个查询都要逐文档读取、反序列化、字段匹配、排序 —— 全部在 CPU 上硬算。加对索引后,B-tree 直接定位数据位置,跳过大量文档解析和内存排序,docsExamined 从百万级降到几十,CPU 负载自然断崖下降。
哪些查询字段必须建索引?
不是所有 WHERE 字段都值得建索引。优先覆盖以下三类:
-
filter中高频出现、且选择性高的字段(如user_id、order_no),避免低基数字段单独建索引(如status: {0,1,2}) - 带
sort的字段,尤其是millis> 300 且hasSortStage: true的慢日志条目 —— 表示 MongoDB 在内存里做了完整排序 - 同时出现在
filter和sort中的组合,例如{dateType: 2, companyCode: "GMP"}+{scanDateTime: -1},应建复合索引{dateType: 1, companyCode: 1, scanDateTime: -1}
注意:sort 字段必须放在复合索引的最后,否则无法跳过内存排序;如果查询还带 limit,索引覆盖越全,CPU 越省。
如何验证索引是否生效?
别只看 db.getIndexes(),要实测执行计划:
- 用
db.collection.explain("executionStats").find({...})查看executionStats.nReturned和executionStats.docsExamined—— 两者越接近,说明越精准命中 - 检查
executionStats.executionStages.stage是否为IXSCAN,而非COLLSCAN或SORT - 留意
executionStats.executionStages.inputStage.stage,如果它里面还有SORT,说明索引没覆盖排序,得调整字段顺序 - 线上加索引前,先在副本集的 Secondary 节点上
db.runCommand({createIndexes: ..., background: true}),避免主节点锁表卡住业务
容易被忽略的 CPU 杀手:冗余索引与表达式索引
索引不是越多越好。以下情况反而推高 CPU:
- 多个相似前缀的复合索引,比如已有
{a:1,b:1,c:1},又建了{a:1,b:1}—— 后者几乎不被使用,却增加写入开销和内存压力 - 对计算字段建普通索引(如
{ $expr: { $gt: [{ $size: "$tags" }, 5] } }),MongoDB 无法利用,仍走 COLLSCAN;改用partialFilterExpression或预计算字段(如存tag_count: 7)再建索引 - 时间范围查询用了
Date对象但索引是字符串类型,导致隐式类型转换,索引失效 —— 检查explain中的indexBounds是否为空
真正省 CPU 的索引,是让 docsExamined ≈ nReturned 的那一个,而不是看起来“全面”的那一组。


















