find()未走索引需用explain("executionstats")验证,关注totalkeysexamined>0、stage为ixscan;复合索引须满足最左前缀原则;hint()仅用于调试,不可长期使用;node.js中需确保真正执行查询并检查数据库/集合名是否正确。

为什么 find() 查询没走索引?先看 explain("executionStats")
直接执行 collection.find({gender: "M"}).explain("executionStats"),别用默认的 queryPlanner 模式——它只模拟计划,不真正执行,容易误判“命中了索引”但实际没用上。
关键看返回里的这几个字段:
-
nReturned:实际返回几条文档 -
totalDocsExamined:扫描了多少文档(越接近nReturned越好) -
totalKeysExamined:用了多少索引条目(>0 才算真走索引) -
executionStages.stage:如果是IXSCAN表示走了索引;COLLSCAN就是全表扫,索引失效
常见假象:cursor: "BtreeCursor" 出现在旧版输出里 ≠ 走了你建的索引,它可能只是用了 _id_ 索引做排序或跳过,得结合 indexBounds 和 keyPattern 看具体用了哪个索引。
复合索引为啥不生效?检查最左前缀和查询字段顺序
你建了 {name: 1, age: -1},但写 find({age: 25}) 或 find({age: 25, name: "Alice"}),前者完全不走索引,后者才可能走——MongoDB 不会跳过第一个字段去匹配后面的。
即使你写 find({age: 25, name: "Alice"}),MongoDB 会自动重排条件顺序,等价于 {name: "Alice", age: 25},所以能命中。但注意:
根据 package.json 中的 engines 字段自动检测 Node.js 版本需求,未安装 nvm 时自动安装并切换至对应版本。支持 Windows、macOS、Linux。适用于 Node.js 项目开发、版本切换和环境配置。
- 如果查询里只有
age,哪怕索引存在,也 不会 用 - 如果查
{name: "Alice", status: "active"},而索引是{name: 1, age: -1},status不在索引里,MongoDB 可能仍用该索引查name,再内存过滤status,此时totalDocsExamined > nReturned - 排序字段没包含在索引里(比如
sort({age: -1})但索引是{name: 1}),就会触发inMemorySort,大数据量直接报错
hint() 强制走索引有用吗?要看是否真解决瓶颈
hint() 是调试手段,不是优化终点。比如你怀疑优化器选错了计划,可以加 .hint({gender: 1, user_name: 1}) 再跑一次 explain("executionStats") 对比 executionTimeMillis 和 totalDocsExamined。
但要注意:
- 如果强制后
totalDocsExamined没降,说明这个索引本身覆盖不了查询需求(比如缺投影字段或排序字段) - 生产环境别长期留
hint(),它绕过优化器,一旦数据分布变化,可能反而更慢 -
hint()传的对象必须和createIndex()的键模式完全一致(包括方向),{gender: 1}不能匹配{gender: 1, user_name: 1}索引
Node.js 驱动里容易漏掉的细节
在 Node.js 中调用 explain() 必须确保操作已真正发起——比如用 toArray() 或 next() 触发执行,否则只拿到计划没统计:
const cursor = collection.find({gender: "M"});
// ❌ 这样只返回 queryPlanner,没执行
console.log(await cursor.explain());
// ✅ 正确方式:先执行再 explain
console.log(await collection.find({gender: "M"}).explain("executionStats"));
另外注意驱动版本:mongodb@4.x+ 已废弃 ensureIndex(),必须用 createIndex();旧代码里如果还混着 ensureIndex(),在 MongoDB 5.0+ 会静默失败,索引根本没建上。
最常被忽略的一点:索引建在集合上,但查询时连错了数据库或集合名——db.collection.find() 在 shell 里看着对,Node.js 里却可能是 db.collection("users_v2") 查的是另一个空集合,自然没索引可用。










