MongoDB分片集群在业务初期规划的分片数量,往往撑不过两三年的数据增长。当存储水位告警频繁触发、写入延迟明显抬升时,扩容就成了绕不开的操作。整个扩容过程的核心是两件事:把新的分片正确加入集群,以及让已有数据平稳地迁移到新分片上。这两步任何一步出问题,都可能导致线上查询抖动甚至数据分布严重倾斜。本文按照实际运维操作的顺序,把判断时机、执行步骤、均衡控制几个环节逐一拆解。

一、什么情况下需要对分片集群扩容
扩容不是拍脑袋决定的事,需要先看几个关键指标。第一个是磁盘使用率,如果各分片的数据盘使用率长期超过70%,就要开始评估了,因为MongoDB的WiredTiger存储引擎在压缩、checkpoint时还需要额外空间,磁盘打满会直接导致节点只读甚至崩溃。
第二个信号是写入延迟。可以通过db.serverStatus()观察opcounters中的insert、update速率,结合mongostat工具看各分片的负载分布。如果某个分片持续成为热点,而另一些分片很空闲,这种情况优先考虑调整分片键或者手动split chunk,而不是盲目扩容。只有当所有分片整体负载都偏高时,加新分片才有意义。
第三个要检查balancer的状态。很多人在扩容前忽略了这一点,结果新分片加进去后数据迟迟不迁移过去。可以先连到mongos执行确认:
// 连接到mongos后执行 sh.status() // 查看balancer是否开启 sh.getBalancerState() // 如果balancer被关闭了,先开启 sh.startBalancer()
另外还要确认当前的chunk大小配置。从MongoDB 6.0开始默认chunk size是128MB,早期版本是64MB。chunk越小,迁移粒度越细,均衡过程越平滑,但元数据量越大。扩容前用use config再查db.settings.find()确认当前配置,做到心里有数。
二、新增分片的具体操作步骤
新增分片的第一步是搭建一个标准的副本集。假设集群现有shard01、shard02,现在要加shard03,先在三台机器上分别启动mongod实例,配置文件中sharding.clusterRole要设置为shardsvr,注意这个参数在旧版本里叫sharding.role,写错了会导致节点起不来。
副本集初始化完成后,先在这个副本集内部验证主从切换、数据写入都正常,再把它挂到集群上。挂载动作在mongos上执行:
// 连接mongos,端口一般是20000或由配置决定
mongo --host mongos.ippipp.com --port 20000
// 将新副本集加入集群,注意写副本集名称和成员列表
sh.addShard("shard03/192.168.10.31:27018,192.168.10.32:27018,192.168.10.33:27018")
// 验证分片是否加入成功
db.adminCommand({ listShards: 1 })
执行addShard时常见两类报错。一是提示can't add shard因为副本集名称与已有分片重名,改名后重试即可。二是提示主机名解析失败,这通常是因为新节点的hosts解析没有同步到所有配置服务器和mongos所在机器,补齐解析记录后重试。还有一种情况是新副本集里残留了测试数据,MongoDB要求空副本集才能加入,需要先清空数据目录。
分片加入成功后,sh.status()的输出里能看到新的shard记录。此时新分片上还没有任何chunk,数据分布严重不均衡,balancer会自动开始工作。如果集群数据量很大,建议先不要急着观察,给balancer一点时间,下一节讲如何控制它的节奏。
三、扩容后的数据均衡与业务影响控制
balancer的工作逻辑是:当集群中数据量最多和最少的分片之间的差异超过迁移阈值(默认是3个chunk),balancer就会选择从多的分片向少的分片迁移chunk。迁移一个chunk的过程包括clone数据、追oplog、提交元数据几个阶段,在目标分片上属于典型的读加写操作,会消耗一定的IO和网络带宽。
对线上业务来说,最担心的就是chunk迁移影响正常请求。控制手段主要有三个。第一个是设置balancer的活动窗口,让迁移只在业务低峰期进行:
use config
// 只允许凌晨1点到5点之间做均衡
db.settings.update(
{ _id: "balancer" },
{ $set: { activeWindow: { start: "01:00", stop: "05:00" } } },
{ upsert: true }
)
// 观察迁移进度,查看当前正在迁移的chunk
db.currentOp({ "active": true, "secs_running": { $gt: 1 }, "desc": /migration/ })
第二个手段是调整chunk迁移对带宽和IO的占用。在分片节点配置中可以设置sharding.secondaryThrottle,让迁移写入等待大多数副本集成员确认,牺牲迁移速度换取从库安全。还可以通过_migrateSourceThrottle相关参数限制迁移速率,不过这些参数在不同版本差异较大,调整前务必核对自己版本的官方文档。
第三个手段是分批推进。如果存量数据有几十TB,一次性均衡可能要跑好几天,期间一旦有节点重启,迁移会重试造成额外开销。稳妥的做法是:先把部分数据库或集合的balancer保持开启,其他暂时关闭,均衡完一批再放下一批。用sh.disableBalancing("库名.集合名")可以针对单个集合关闭均衡,粒度比全局开关更细。
判断均衡是否完成,看sh.status()里各分片的chunk数量是否接近,或者直接查config库的chunks集合统计分布。均衡完成后建议持续观察几天mongostat的输出,确认新分片确实分摊了读写流量,而不是只存了数据却没接到请求,这种情况通常意味着分片键设计有问题,读写仍集中在旧分片上,需要从架构层面重新审视。
四、扩容相关的注意事项与总结
有几点容易踩坑的地方需要强调。第一,扩容前务必确保配置服务器副本集(CSRS)是健康的,config server出问题的话整个集群元数据操作都会卡住,addShard根本执行不了。第二,新分片的MongoDB版本不要低于现有集群,最好保持小版本一致,避免副本集协议或元数据格式不兼容。第三,扩容操作要在业务低峰期启动,虽然加分片本身是在线操作,但随后的均衡迁移持续时间内负载会上升。
还要区分扩容和缩容的语义。用removeShard摘除分片时,MongoDB会先把该分片上的chunk全部迁走才能移除,耗时不比扩容短。而扩容只是加入新分片,存量数据靠balancer慢慢摊过去,两者方向相反但都依赖均衡机制。所以不管加还是减,balancer窗口管理都是核心技能。
整体来看,MongoDB分片集群扩容的操作本身并不复杂,难点在于对均衡过程的节奏控制和业务影响评估。建议在测试环境完整演练一遍,把每个命令的输出和报错都过一遍手,再到生产环境执行。扩容完成后也别忘了更新监控告警的阈值,让新的集群容量在监控体系里得到正确反映,为下一次容量规划留出数据依据。
MongoDB分片集群MongoDB扩容数据均衡修改时间:2026-09-08 00:34:39