collscan仅表示当前查询未使用索引,原因包括最左前缀不满足、字段类型不匹配或隐式转换等;需结合explain("executionstats")中totaldocsexamined与nreturned比值判断索引实际效果。

explain()里看到COLLSCAN,先别急着建索引
COLLSCAN不是索引不存在的铁证,而是“当前查询没用上索引”的明确信号。它只说明优化器放弃了索引,原因可能是索引存在但不匹配、字段类型不一致、或查询条件破坏了最左前缀约束。直接建新索引反而可能掩盖真实问题。
检查WHERE条件是否满足最左前缀原则
复合索引 {a: 1, b: 1, c: 1} 只有在查询中包含 a 字段时才可能生效;仅查 b 或 c,哪怕 possible_keys 显示该索引,key 也必为 NULL。
- ✅ 有效:
{a: "x"}、{a: "x", b: "y"}、{a: "x", b: "y", c: "z"} - ❌ 无效:
{b: "y"}、{c: "z"}、{b: "y", c: "z"} - ⚠️ 半失效:
{a: "x", c: "z"}——b缺失导致c无法走索引,实际只用到a部分
警惕字段类型和隐式转换导致索引失效
索引列是 String,但查询传入数字或布尔值,MongoDB 会做隐式类型转换,结果就是 key: null。同样,带空格的字符串、大小写敏感 collation 不一致、或使用 $regex 时加了 i 选项,都会让 B-tree 索引失效。
- ❌
db.users.find({age: "25"})(age 是Number)→ 类型不匹配 - ❌
db.users.find({name: {$regex: /john/i}})→i破坏二进制可比性 - ❌
db.users.find({name: {$regex: "john"}})→ 无^前缀,全表扫 - ✅
db.users.find({name_lower: {$regex: "^john"}})(name_lower已建索引且小写归一化)
用executionStats确认索引是否真起作用
explain("executionStats") 比默认的 queryPlanner 更可靠:看 totalDocsExamined 和 nReturned 是否接近。差一个数量级,说明索引只是“定位”而非“过滤”,比如前缀太短(^A)或数据分布极不均匀。
- ✅
"totalDocsExamined": 42, "nReturned": 42→ 索引高效 - ⚠️
"totalDocsExamined": 89200, "nReturned": 42→ 索引命中但过滤能力差,得换策略(如预处理字段、全文索引) - ❌
"totalDocsExamined": 1200000, "totalKeysExamined": 0→ 根本没走索引,回到前两步排查
explain("executionStats") 足以暴露问题,但读懂数字背后的含义,需要对照字段定义、数据样本和查询意图三者交叉验证。











