默认explain不显示分片信息是因为它仅使用"queryplanner"模式,只返回mongos路由层的计划,不展示各分片实际执行细节(如ixscan/ collscan、耗时、扫描文档数),需用"allplansexecution"或runcommand才能获取分片级executionstats。

分片集群里直接用 db.collection.find().explain() 看不到各分片的执行细节,必须用 "allPlansExecution" 模式或 runCommand 才能暴露真实分片行为。
为什么默认 explain 不显示分片信息
在分片集群中,db.collection.find().explain() 默认使用 "queryPlanner" 模式(即使不显式传参),它只返回 mongos 路由层选中的“最终计划”,而隐藏了:每个分片是否真的执行了 IXSCAN、有没有某个分片退化为 COLLSCAN、哪个分片返回了最多文档。这些信息对定位慢查询根源至关重要。
常见错误现象是:总耗时高,但 executionTimeMillis 显示很低 —— 实际是 mongos 汇总阶段耗时没被计入,或者多个分片并行执行的耗时被平均化了。
-
db.collection.explain("queryPlanner"):只告诉你“mongos 计划怎么发请求”,不告诉你“分片实际怎么干” -
db.collection.explain("executionStats"):会显示汇总后的totalDocsExamined和nReturned,但无法区分各分片贡献 -
db.collection.explain("allPlansExecution"):强制触发所有候选计划执行,并在shards字段下逐一分片列出executionStats,这才是真·分片级视图
如何获取每个分片的完整执行统计
最可靠的方式是用 runCommand 显式调用 explain 命令,并指定 verbosity: "allPlansExecution":
db.runCommand({
explain: {
find: "orders",
filter: { status: "pending", createdAt: { $gt: ISODate("2026-01-01") } }
},
verbosity: "allPlansExecution"
})
返回结果中会出现 shards 字段,结构类似:
"shards": {
"shard01/10.0.1.10:27018": { ... },
"shard02/10.0.1.11:27018": { ... }
}
每个分片对象内都包含完整的 executionStats,重点关注:
-
executionStats.executionTimeMillis:该分片本地执行耗时(不是网络往返) -
executionStats.totalDocsExamined:该分片实际扫描文档数(若远大于nReturned,说明该分片缺失有效索引) -
executionStats.executionStages.stage:比如SHARDING_FILTER后跟IXSCAN是正常;若出现COLLSCAN就得立刻查该分片的索引
容易被忽略的分片特有阶段和字段
分片环境下 explain 输出中会出现非单机环境特有的执行阶段,它们直接反映路由与合并逻辑:
-
SHARD_MERGE:mongos 正在合并来自多个分片的结果 —— 如果这个阶段耗时高,说明结果集太大或排序/跳过操作未下推到分片 -
SHARDING_FILTER:mongos 过滤掉不属于本分片的数据(例如根据 shard key 匹配),此阶段本身很快,但若它上游是COLLSCAN,说明该分片没走索引,纯靠过滤硬扛 -
stage: "SINGLE_SHARD"或"MULTI_SHARD":出现在queryPlanner.winningPlan中,表示 mongos 判断是否能路由到单一分片 —— 若本该SINGLE_SHARD却走了MULTI_SHARD,说明查询条件没包含 shard key,导致广播查询
注意:executionStats.totalKeysExamined 在分片中可能为 0 —— 因为某些版本的分片节点不汇报索引键扫描数,此时只能依赖 totalDocsExamined 和阶段类型交叉判断。
聚合管道(aggregate)分片分析要额外加 $shardFilter
对 aggregate 使用 explain 时,如果管道里没显式包含 $shardFilter 阶段,mongos 可能无法下推部分操作,导致中间结果在 mongos 内存中膨胀。验证方式:
- 先运行
db.collection.explain("allPlansExecution").aggregate([...]) - 检查每个分片的
executionStages是否包含$shardFilter(通常由 mongos 自动注入) - 若缺失,且你用了
$lookup或$unwind等内存敏感操作,务必确认分片键是否出现在$match开头,否则整个 pipeline 可能在 mongos 上执行
真正麻烦的是那些跨分片聚合后才 $sort 或 $skip 的场景 —— explain 会清楚显示 SHARD_MERGE 后接 SORT,这种无法避免全量拉取,只能靠缩小分片数据范围或改用覆盖索引缓解。











