sh.status() 默认只显示 chunk 数量而非真实数据量,需结合 verbose 模式、db.collection.stats() 直连分片查询 size/storagesize/count,或通过 splitvector 分析键分布来判断倾斜。

sh.status() 输出里怎么看分片数据倾斜
sh.status() 是最直接的入口,但它默认只展示每个分片的 chunk 数量和分布,不显示真实数据量。很多人扫一眼看到 chunks: 120 就以为均匀了,其实 chunk 数 ≠ 数据量 —— 一个 chunk 可能存 1KB 日志,也可能塞满 64MB 的大文档。
关键要看输出中每个分片下方的 balancer 状态和 mongos 汇总行里的 chunks + docs(如果有开启 verbose)。但默认不显示 docs,得手动补查:
- 先确认是否启用了
sh.enableSharding("db")和sh.shardCollection("db.coll", {key: 1}),否则sh.status()不会列出集合级分片信息 - 运行
sh.status({verbose: true}),部分 MongoDB 版本(4.4+)会额外显示各分片的近似文档数(docs)和大小(size),但注意这是采样估算,不是精确值 - 如果没
docs字段,说明当前版本或配置未启用详细统计,得换方法
用 db.collection.stats() 查单个分片的真实数据量
想确认某个分片上某集合到底占多少空间,不能只信 sh.status(),得连到对应 shard 的 mongod 实例上查 —— 因为 stats() 返回的是该节点本地存储的真实值。
操作步骤是:
- 从
sh.status()输出中找到目标分片名,比如shard0001 - 用
mongo --host shard0001:27018直连该分片(端口看实际部署) - 切换到对应库:
use db - 执行
db.coll.stats({scale: 1024*1024}),scale单位是字节,这里转成 MB 方便对比 - 重点看
size(原始数据大小)、storageSize(磁盘占用)、count(文档数)三项
注意:不同分片的 coll.stats() 结果不能直接加总,因为可能有未迁移完的 chunk 或 orphaned 文档;且 storageSize 受压缩、WiredTiger page 大小影响,同一份数据在不同分片上数值可能差 10%–20%。
聚合查询统计各分片的 chunk 数据量差异
如果要批量看所有分片上某集合的 chunk 级数据分布,sh.status() 不够细,得查 config.chunks 配置库 —— 但这里只存 chunk 范围和归属分片,没有大小信息。真正能算出“每个 chunk 实际多大”的办法,是结合 config.chunks 和各分片的 db.runCommand({collStats: ...}),但太重。
更实用的折中方案是:用 db.runCommand({splitVector: "db.coll", keyPattern: {a: 1}, maxChunkSizeBytes: 67108864}) 模拟切分,观察返回的 splitKeys 分布密度,间接判断索引键是否倾斜。常见坑:
-
maxChunkSizeBytes必须设成和集群实际chunkSize一致(默认 64MB),否则结果失真 - 如果
keyPattern是单字段且写入集中在时间戳/自增 ID,splitVector会返回大量靠前的 key,说明新数据全打在一个分片上 - 命令必须在
mongos上执行,不能直连 config server
为什么 balancer 没自动均衡?检查这几个点
发现数据明显不均,但 sh.status() 显示 balancer: Currently enabled: yes,却迟迟不搬 chunk —— 这通常不是 bug,而是被卡住了。
优先检查:
- 执行
sh.getBalancerState()和sh.isBalancerRunning(),确认 balancer 真正在跑(有时它启了但没干活) - 查
config.settings.find({ _id: "balancer" }),看有没有stopped: true或设置了activeWindow限制时段 - 运行
sh.getBalancerLocks(),返回非空数组说明有锁,可能是上次迁移中断遗留,需手动sh.stopBalancer()再sh.startBalancer() - 检查各分片磁盘剩余空间:
db.runCommand({serverStatus:{}}).metrics.record.size对比db.runCommand({getFreeStorageSize:{}}),如果某分片剩余
最容易被忽略的是:chunk 迁移需要源分片和目标分片同时在线且网络通,而 sh.status() 不报这类底层异常,得去对应分片日志里搜 moveChunk 和 failed。










