应使用 explain("executionStats") 查文档所在分片,其 shards 字段显示实际路由的分片名及子查询详情;仅当查询含完整分片键等值条件时,shards 才只含一个分片,否则可能广播至全部分片。

怎么用 explain() 查文档落在哪个分片
直接在查询语句后链式调用 explain("executionStats") 或 explain("queryPlanner"),但注意:**只有 explain("executionStats") 会显示实际路由到的分片信息**。MongoDB 的分片路由逻辑发生在 mongos 层,explain 返回结果里的 shardFilter、shards 字段才是关键。
-
shards是一个对象,键是分片名(如"shard0001"),值是该分片上执行的子查询详情;如果只有一项,说明查询被路由到了单一分片 - 若查询带了分片键等值条件(如
{_id: ObjectId("...")}或{tenantId: "abc"}),通常只会打到一个分片,shards对象里就只有一个 key - 若没命中分片键或用了范围/无索引查询,
shards可能包含全部分片,此时 MongoDB 会广播查询——这不是你“查文档在哪”,而是“它可能在任意一个” - 别用
explain("queryPlanner"),它不触发真实执行,也就没有shards字段
为什么 getShardForDoc() 不是标准 API
MongoDB 官方驱动和 shell 都没提供类似 getShardForDoc() 这样的函数。分片归属由 mongos 根据分片键值 + 分片集群当前 chunk 分布实时计算,不是文档自带元数据。你不能“读一个文档就知道它在哪儿”,除非你手动复现路由逻辑。
- 需要自己解析分片键(
sh.status()或db.getSiblingDB("config").shards.find()查分片列表,再查config.chunks找对应 range) - 对目标文档的分片键值做哈希(若为 hashed 分片)或范围比对(range 分片),才能推断归属
- 这个过程容易出错:chunk 可能正在迁移、balancer 处于活跃状态、或者你查的 config 数据已过期
- 所以生产环境别靠手算,优先走
explain("executionStats")看真实路由结果
常见错误:查询没走分片键,explain 却显示多个分片
典型现象是执行 db.orders.find({status: "pending"}).explain("executionStats") 后,shards 字段列出了所有分片,且每个分片都返回了 nReturned: 0 或少量结果。这不是 bug,是预期行为。
- 因为
status不是分片键,mongos 无法预判数据分布,只能把查询发给所有分片(scatter-gather) - 即使最终只有一两个分片有匹配文档,其他分片也得参与扫描——这就是全分片扫描(full scatter-gather)
- 这种查询无法用于定位单个文档所属分片,它回答的是“哪些分片响应了这次查询”,而非“这个文档物理存在哪”
- 想精准定位,必须确保查询条件中包含完整的分片键等值匹配(如
{tenantId: "org-123", _id: ObjectId("...")})
绕不开的坑:chunk 迁移期间 explain 结果不可信
当 config server 正在汇报 chunk 迁移(比如 moveChunk 命令执行中),explain("executionStats") 返回的 shards 可能反映的是迁移前的路由,而实际数据已在迁入分片上。这时候查到的“所在分片”可能是错的。
- 检查迁移状态:运行
sh.isBalancerRunning()和db.getSiblingDB("config").locks.find({_id: "balancer"}) - 看是否有未完成的迁移:查
config.migrations,状态为"committed"才算真正落地 - 临时规避办法:在业务低峰期操作,或加一层重试——先
explain,再根据结果去对应分片上用db.collection.findOne()实锤确认 - 别依赖一次
explain就下结论,尤其在线上诊断数据漂移问题时
分片归属不是静态属性,它依赖实时路由逻辑、配置状态和底层 chunk 分布。想靠一次命令拍板“这个文档在哪个分片”,本质上是在问一个动态系统里的瞬时快照——得小心它刚被刷新过。

















