sh.status() 是查 chunk 分布和热点的第一入口,可识别 chunk 数异常、跨度大或长期未迁移的 shard;需结合 sh.getShardDistribution() 和 explain("executionStats") 区分数据热点与查询热点。

sh.status() 是查 chunk 分布和热点的第一入口
它不直接标出“热点”,但能让你一眼看出哪些 shard 上 chunk 数异常多、哪些 chunk 跨度极大或长期未迁移。重点盯 shards 下每个分片的 chunks 数,再进 chunks 列表看每个 chunk 的 min/max 和所在 shard。如果某个 shard 的 chunk 数远超其他(比如 80 个 vs 其他 12 个),基本就是写入或查询集中点。
常见误判:只看 docs 总数。实际热点常表现为小范围 key 高频更新(如 { userId: 1001, seq: 123456 }),导致单个 chunk 持续被写满、分裂、再被写满——这时 chunks 数可能没变,但该 chunk 的 lastmod 时间戳频繁刷新,且 size 接近 128MB 默认上限。
- 执行前确保已连接
mongos,不是任意mongod - 若输出中
balancer状态为off,chunk 不均衡不能归因于负载不均,而是迁移被阻断 - 注意
tags字段:带 tag 的 chunk 若长期卡在非目标 shard,说明balancer未按预期触发迁移
db.collection.stats() 在分片集群里会误导你判断热点
它只返回 primary shard 的局部统计,count、size、nindexes 全是单分片数据。你以为某集合“很大”,其实只是 primary shard 上存了 90% 文档;你以为某字段“索引少”,其实其他 shard 已建好覆盖索引但你根本看不到。
真正要定位热点 chunk,得用 sh.getShardDistribution() —— 它会主动向所有 shard 发起 collStats 并汇总,输出每分片的 docs、dataSize、storageSize。如果某 shard 的 docs 是其他 shard 的 5 倍以上,且 storageSize 明显偏高(比如含大量 padding 或未压缩的旧文档),那就是典型热点候选。
- 必须 MongoDB ≥ 4.2,否则该函数不存在
- 目标集合必须已启用分片,否则报错
collection is not sharded - 某 shard 临时不可达时,该函数跳过并警告,不影响其余结果,但你要手动补查那个 shard
explain("executionStats") 能暴露广播查询引发的隐性热点
很多“热点”不是数据分布不均,而是查询模式导致某分片反复被广播扫描。比如 db.collection.find({ status: "processing" }) 不带分片键,mongos 就得把请求发给所有 shard。这时最慢的那个 shard 的 executionTimeMillis 会拖垮整体响应,而它的 totalDocsExamined 可能接近全量,形成事实上的 I/O 热点。
执行 db.collection.find({ ... }).explain("executionStats") 后,在 executionStages 下逐个展开每个 shardName 对象,重点关注:
-
nReturned:该 shard 实际返回几条(若多个 shard 都 > 0,就是广播) -
totalDocsExamined:该 shard 扫了多少文档(若接近其docs总数,说明没走索引) -
executionTimeMillis:单 shard 执行耗时(找出拖尾的那个)
别信 queryPlanner.winningPlan 里的“IXSCAN”,那只是计划,不是实际执行路径。
手动连 shard 查单个 chunk 的真实负载
当你通过 sh.status() 锁定一个可疑 chunk(比如 min: { ts: ISODate("2026-08-01"), id: 1000 }, max: { ts: ISODate("2026-08-01"), id: 1999 }),下一步不是等 balancer,而是直连对应 shard 的 mongod 实例(非 mongos),执行:
db.collection.find({ ts: { $gte: ISODate("2026-08-01"), $lt: ISODate("2026-08-02") }, id: { $gte: 1000, $lt: 2000 } }).explain("executionStats")
这样能看到真实索引使用、内存压力、锁等待。如果 executionStats.executionTimeMillis 远高于同类查询,且 executionStats.totalKeysExamined 异常高,说明这个 chunk 内部存在低效索引或数据倾斜(比如 1000 条里 900 条都落在同一个 id 值上)。
注意:直连 shard 时,db.collection 必须是真实库名+集合名,不能用 use 切库后省略前缀;否则命令会发到 local 库或报错。
真正的难点不在工具,而在区分“数据热点”和“查询热点”。前者改分片键或加 tag range,后者必须收敛查询条件或补索引。很多人花一周调优 chunk 分布,最后发现慢的是一个漏掉分片键的定时任务——查之前,先 explain 三秒。


















