MongoDB 6.0/7.0 中不存在独立的“索引分析器”,需组合使用 db.setProfilingLevel() 开启慢查询捕获、explain("executionStats") 分析执行计划、$indexStats 识别低效索引,并严格遵循等值→范围→排序的联合索引字段顺序。
直接结论:MongoDB 6.0/7.0 中没有叫“索引分析器”的独立组件;你真正要用的是 db.setProfilingLevel() + explain() + $indexStats 这套组合,缺一不可。
Profiler 是唯一能告诉你“哪条查询慢、为什么慢”的入口。6.0/7.0 默认关闭,必须手动开启,且级别选错就等于白开:
db.setProfilingLevel(1, { slowms: 50 }) 最常用:只记录 ≥50ms 的操作,写入 system.profile 集合,对线上影响可控db.setProfilingLevel(2) 别在生产环境用:所有操作都记,system.profile 很快撑满(默认仅 1MB),还会拖慢写入operationProfiling.mode: "slowOp" + operationProfiling.slowOpThresholdMs: 50,不是 slowms
db.getProfilingStatus() 返回 { "was": 1, "slowms": 50, ... } 才算生效光看 system.profile 里的 millis 和 ns 不够——它不告诉你用了哪个索引、扫了多少文档。必须对慢查询语句补上 explain("executionStats"):
executionStats.executionStages.stage:要是 "COLLSCAN" 就说明完全没走索引executionStats.nReturned 和 executionStats.totalDocsExamined:如果后者远大于前者(比如 10万 vs 10),大概率是索引没覆盖查询字段或顺序不对executionStats.executionStages.inputStage.keyPattern:确认实际命中的是你预期的那个索引名,而不是 company_id_hashed 这种哈希索引——它对范围查询无效explain("allPlansExecution"),但别滥用:会真实执行所有候选计划,可能锁表或加重负载建一堆索引却不清理,写性能会肉眼可见变差。6.0/7.0 用 $indexStats 聚合阶段查真实使用频率:
db.collection.aggregate([ { $indexStats: {} }, { $sort: { "accesses.ops": 1 } } ]) 直接按访问次数升序排,底部就是“僵尸索引”accesses.ops 是自 MongoDB 3.2 起统计的命中次数,为 0 且存在半年以上,基本可删keyPattern 重复的索引:比如已有 {a:1,b:1},又建了 {a:1,b:1,c:1},前者大概率冗余db.collection.getIndexes() 检查是否有 hint() 或应用层硬编码依赖该索引名很多慢查询根源不在有没有索引,而在字段顺序。6.0/7.0 的查询优化器仍严格遵循“等值 → 范围 → 排序”原则:
{ company_id: 13272, create_time: { $gte: ..., $lte: ... } },索引必须是 { company_id: 1, create_time: 1 },反过来就失效sort({ status: 1 }),理想索引是 { company_id: 1, create_time: 1, status: 1 },把排序字段塞到最后"hashed")只适合等值查询,$gte/$in 等一律绕过,别把它和范围字段混建联合索引db.collection.find(...).explain("executionStats") 确认 stage 变成 "IXSCAN" 且 totalDocsExamined ≈ nReturned
真正卡住人的地方往往不是命令怎么敲,而是 profiler 开了却没查 system.profile,或是 explain 看了却忽略 totalDocsExamined 和 keyPattern 的匹配关系。索引不是建得越多越好,而是让每一条慢查询都能精准落到一个最小必要索引上。