sh.shardCollection() 无反应是因为它仅注册分片元数据且不自动迁移数据;必须先为分片键创建索引,否则报错“cannot shard collection without an index”。

sh.shardCollection() 为什么执行后没反应?
不是命令没生效,而是它只负责注册分片元数据,不自动迁移数据。集合必须有索引才能分片,且该索引必须是分片键(shard key)——sh.shardCollection() 要求的 key 参数必须已存在于集合的索引中(包括 _id 索引),否则直接报错 "cannot shard collection without an index"。
- 先用
db.collection.getIndexes()确认目标字段已有索引;没有就建:db.collection.createIndex({ "uid": 1 }) - 分片键一旦选定无法更改,也不能是数组字段或嵌套对象中的非标量值(如
{"tags.0": 1}不合法) - 如果集合非空,
sh.shardCollection()会触发初始 chunk 拆分和迁移,但过程异步,需用sh.status()观察chunks分布和balancer状态
分片键选 _id 还是业务字段?
_id 默认唯一且有索引,看似省事,但默认 ObjectId 是时间戳+机器码+进程号+计数器,高位单调递增,所有新文档都写入同一个 chunk,导致热点问题。业务字段(如 user_id、tenant_id)更可控,但要注意分布均匀性。
- 避免用低基数字段(如
status: {0, 1, 2}),会导致 chunk 倾斜 - 复合分片键(如
{"tenant_id": 1, "created_at": 1})可兼顾路由效率与数据分散,但查询时必须带前缀字段才能命中分片路由 - 哈希分片(
{"uid": "hashed"})能强制均匀分布,但失去范围查询能力,且不支持多字段哈希
执行 sh.shardCollection() 报错 "already sharded" 或 "namespace not found"
前者说明集合已在分片集群中注册为分片集合,重复执行无害但无效;后者通常是因为连接的是 config server 或单个 mongod,而非 mongos 路由节点——sh.shardCollection() 只能在 mongos 上运行,且数据库必须已存在(use mydb 后再执行)。
- 检查当前连接:
db.runCommand({isMaster: 1})返回里要有"msg": "isdbgrid"才是 mongos - 确保数据库名和集合名拼写完全匹配,大小写敏感,且集合名不含特殊字符或空格
- 如果集合刚创建,可能因缓存延迟未被 config server 立即发现,稍等几秒再试
分片后 count() 和 distinct() 性能变慢了?
因为这些操作需要广播到所有分片收集结果再合并,网络开销和协调成本上升。尤其 distinct() 在无索引字段上会全表扫描每个分片。
-
count()尽量加查询条件并确保能命中分片键,否则变成 scatter-gather -
distinct()必须在分片键或已建索引的字段上执行,否则超时风险高 - 聚合管道中慎用
$group和$sort,它们无法下推到各分片并行执行,容易成为瓶颈
分片不是开关一按就完事的事,分片键设计错了,后面扩容、迁移、查询优化全得跟着返工。真正麻烦的从来不是命令怎么敲,而是想清楚数据怎么切、怎么查、怎么均衡。










