目标
MongoDB 会准确告诉你它如何执行查询:剖析器记录慢操作及其计划摘要,带 executionStats 的 explain 统计被检查的每个文档。COLLSCAN 意味着全集合扫描,扫描后再接 SORT 意味着在内存里排序全部结果。在写任何索引之前先读这两个标签,是“修好查询”与“给查询贴装饰”的区别。
步骤要点
- setProfilingLevel(1, { slowms: 100 }) 只记录超过 100 毫秒的操作,避免噪声与额外开销。
- 从 system.profile 按 millis 降序取前 5 条,关注命名空间、查询形状与 planSummary 中的 COLLSCAN 标记。
- 对最差形状执行 explain(“executionStats”):看到 COLLSCAN + SORT 即确认无索引覆盖过滤且排序在内存完成。
- 建复合索引 { customerId: 1, status: 1, shippedAt: -1 }:等值字段打头、排序字段按查询方向殿后。
- 重建 explain:winningPlan 变 IXSCAN、totalDocsExamined 从百万级坍缩到返回数、executionTimeMillis 同步下降。
排错提示
profiler 写入影响生产就上调 slowms 而不是关掉它,一级剖析在高阈值下开销可忽略;索引存在却仍 COLLSCAN 是查询形状不匹配索引前缀,等值字段必须构成复合索引的前缀;计划在多个索引间摇摆说明存在前缀相近的竞争索引,应合并成更宽的一条并删冗余。另外 system.profile 是封顶集合、会遗忘旧条目,需要历史就导出。
适用边界
docsExamined 才是关键比率,examined/returned 高达百万倍时加硬件无济于事;executionStats 受缓存冷暖影响,对比计划要在相近缓存条件下进行。
代码示例
// Profiler: only the slow stuff
db.setProfilingLevel(1, { slowms: 100 })
// The shape that hurts
db.orders.find({ customerId: 4711, status: "shipped" })
.sort({ shippedAt: -1 })
.explain("executionStats")
// The fix: equality prefix, then sort field
db.orders.createIndex({ customerId: 1, status: 1, shippedAt: -1 })