预分片+bulkWrite+ordered:false是释放分片集群写入吞吐的唯一有效组合;因刚分片空集合默认仅1个chunk,所有写入集中于单一分片,须提前预分片并配合无序批量写入才能实现真正并行。
预分片 + bulkWrite + 无序模式,是唯一能真正释放分片集群写入吞吐的组合。其他任何调优(比如加大连接数、调高writeConcern)在默认单chunk结构下都只是掩耳盗铃。
MongoDB对空集合首次分片时,默认只创建1个chunk,范围覆盖整个分片键空间。所有写请求都被路由到持有这个chunk的shard上——哪怕你有16个shard,99%的写压力也压在其中1台。
sh.status()里看到chunks行只有1条,且shard列全为shard0000,就是典型症状balancer不会主动拆分,它只负责迁移已有chunk,不负责“生长”新chunk一旦集合里存在任意一条文档,sh.splitAt()大概率失败,sh.updateZoneKeyRange()也无法覆盖未初始化的键空间。补救操作极易引发块分布倾斜或迁移卡死。
{_id: "hashed"})最稳妥:切点可精确计算,例如8个shard对应7个切点,用NumberLong("0x2000000000000000")这类值{email: 1})必须用sh.updateZoneKeyRange()配合sh.addShardToZone(),且sh.shardCollection()必须最后执行sh.status()中chunks总数应等于预设数量,且每个shard至少分配到1个chunkbulkWrite并设ordered: false
预分片只是铺好了路,如果客户端还串行发insertOne或用mongoimport,mongos仍可能把请求扎堆打到同一个shard。只有bulkWrite开启无序模式,才能触发真正的并行路由。
mongoimport本质是单文档循环插入,不支持ordered: false,千万级导入比bulkWrite慢3–5倍collection.bulk_write(operations, ordered=False),Node.js同理writeErrors字段,无序模式下失败不影响其余操作即使你成功预分了100个chunk,若分片键是单调递增的created_at或自增_id,新数据仍会持续写入最后一个chunk,导致热点回退——这和没预分片没区别。
哈希分片键天然规避该问题;范围分片键必须确保业务数据在键值上均匀分布(比如用email前缀而非完整邮箱),否则预分片只是徒劳。