生产环境应保持 chunkSize 为默认值 64MB,不要主动调小;仅当出现长期未分裂的巨型 chunk(>128MB、jumbo、导致不均衡或查询变慢)时,才可手动分步上调至128MB。

默认64MB是生产环境的合理起点
直接说结论:生产环境应保持 chunkSize 为默认值 64MB,不要主动调小。这不是保守,而是基于分裂开销、迁移稳定性与磁盘空间复用效率的综合权衡。
很多团队在写入压力上升时误以为“切得更细 = 更快均衡”,于是把 chunkSize 改成 16MB 甚至 8MB。结果观察到的是:sh.status() 中 chunks 数量暴增、moveChunk 日志刷屏、部分 shard 上出现大量 orphaned documents,且 db.collection.stats() 显示 storageSize 远大于 size —— 这就是空洞开始堆积的信号。
- 每个 chunk 初始化会预分配数据文件空间,小 chunk 导致碎片化加剧,OS 层无法回收这些“空洞文件”
- 分裂操作本身要加全局锁(虽短暂),高频分裂会拖慢写入吞吐
- balancer 不会自动合并小 chunk,所以碎了就真碎了,只能靠人工干预或等待 TTL 清理(不可控)
什么情况下才考虑上调到128MB
上调是唯一被允许的调整方向,且仅适用于明确存在长期未分裂的巨型 chunk 场景。判断依据不是“看起来大”,而是看它是否持续阻碍均衡和查询性能。
典型触发条件:
-
sh.status()输出中某 shard 的单个 chunk 大小长期 >128MB(比如连续 24 小时以上) - 该 chunk 对应的分片键范围极窄(如
{status: "processing"}占比超 70%),且无法再分裂(jumbo chunk) - 该 chunk 所在 shard 的磁盘使用率已超 85%,而其他 shard 均低于 40%
- 对该 chunk 的范围查询(如
find({ts: {$gt: ISODate("...")}}))响应明显变慢,且执行计划显示全 chunk 扫描
调整必须分步执行:sh.setBalancerState(false) → 在 mongos 上运行 sh.splitAt("db.coll", {"_id": ObjectId("...")}) 手动切分 → 等待 balancer 完成当前轮次(sh.getBalancerStatus() 查 activeWindow 和 stopped)→ 再恢复 balancer。不能直接改配置项。
numInitialChunks 参数只对空集合哈希分片有效
初始化时想控制 chunk 数量,很多人会盯上 sh.shardCollection() 的 numInitialChunks 参数。但它有严格前提:
- 仅适用于空集合
- 仅对哈希分片(
{"field": "hashed"})生效 - 实际生成 chunk 数量 ≈
numInitialChunks的最近 2 的幂次(如设 10,最终生成 8 或 16 个)
如果集合已有数据,这个参数完全被忽略;如果是范围分片({"ts": 1}),它也不起作用。线上扩容或重分片时,别指望靠它“预设均匀度”。真正影响初始分布的是分片键本身的基数和写入模式。
真正该花精力的地方是分片键设计
Chunk 大小只是表象,分片键才是根因。一个高基数、非单调、分布均匀的分片键,能让 64MB chunk 自然维持在健康数量级;反之,哪怕强行设成 128MB,也会迅速退化成 jumbo chunk 或热点 shard。
检查清单:
- 字段去重值是否 ≥ 100,000?用
db.coll.distinct("key").length快速验证 - 写入是否集中在某个时间窗口或某几个取值?比如
status只有 "pending"/"done" 两种,就属于极低基数( - 是否含单调递增字段(如
timestamp、_idObjectId)?单独用必热点,必须配合哈希或复合(如{region: 1, _id: 1}) - 上线前用真实流量压测,
sh.status()中各 shard 的 chunks 数量偏差是否在 ±15% 内?
Chunk Size 是个被动调节器,分片键才是主动控制器。把后者做扎实了,64MB 就是最省心的选择。

















