移除分片前,必须确保其数据已迁移到集群其余分片。本文介绍如何安全迁移数据并移除分片。
关于本任务
执行过程中创建集合、对集合分片或移动集合,可能造成中断及意外结果。
不要用此流程把整个集群迁移到新硬件,应使用自管理分片集群硬件迁移指南。
如果集群中的块分布不均,平衡器会先迁出待移除分片中的块,然后再平衡其余分布。
移除分片可能关闭已有变更流游标,且关闭的游标不一定能够完整恢复。
分片移除期间可以安全重启集群。正在进行的排空操作会在组件重启后自动继续,状态记录于 config.shards。
开始之前
本流程使用 sh.moveCollection() 迁出集合。操作前,请阅读该方法的注意事项与要求,理解其行为。
先使用 mongosh 连接集群中的一个 mongos。
注意:移除多个分片时,应同时移除,而非逐个移除。逐个操作会让平衡器将数据迁到其他剩余分片,而每个分片一次只能参与一次块迁移,因此会限制迁移吞吐量。
1. 确保平衡器启用
迁移数据需要启用平衡器。检查状态:
sh.getBalancerState()
返回 true 表示已启用;返回 false 时,按平衡器启用说明操作。
2. 确定待移除分片名称
运行 listShards:
db.adminCommand( { listShards: 1 } )
shards._id 包含分片名称。
3. 迁出分片中的块
对目标分片运行 removeShard:
db.adminCommand( { removeShard: "<shardName>" } )
注意:mongos 会将此命令的写关注转换为 majority。
返回示例:
{
"msg" : "draining started successfully",
"state" : "started",
"shard" : "<shardName>",
"note" : "you need to drop or movePrimary these databases",
"dbsToMove" : [
"db1",
"db2"
],
"ok" : 1,
"operationTime" : Timestamp(1575398919, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1575398919, 2),
"signature" : {
"hash" : BinData(0,"Oi68poWCFCA7b9kyhIcg+TzaGiA="),
"keyId" : Long("6766255701040824328")
}
}
}
分片进入 draining 状态,平衡器开始把块迁往其他分片。为避免严重影响集群,迁移会缓慢进行。根据网络容量和数据量,可能需要几分钟到几天。
提示:draining 期间可以使用 reshardCollection 将数据重新分布到其他分片,可能比等待平衡器迁移更快。集群保证不会把数据放到任何正在排空的分片。moveCollection 与 reshardCollection 不能同时运行。完整方法见通过重新分片添加与移除分片的指南。
4. 列出已分片集合的命名空间
使用 $shardedDataDistribution 并投影 ns:
use admin
db.aggregate(
[
{ $shardedDataDistribution: { } },
{ $project: { ns: 1 } }
]
)
保存输出,供后续参考。
5. 列出集群全部数据库
db.adminCommand( { listDatabases: 1, nameOnly: true } )
6. 从分片迁出集合
对除 admin 和 config 之外的每个数据库执行以下步骤。
列出数据库集合
排除支持 CSFLE 的内部集合、系统集合、时间序列集合和视图:
use <databaseName>
db.getCollectionInfos(
{
$and: [
{ type: { $nin: [ "view", "timeseries" ] } },
{ name: { $not: { $regex: "^system\." } } },
{ name: { $not: { $regex: "^enxcol_\..*(\.esc|\.ecc|\.ecoc|\.ecoc\.compact)$" } } }
]
},
{ nameOnly: true }
)
移动需要迁移的集合
对 getCollectionInfos() 返回的每个集合依次操作。
注意:一次只能进行一个 moveCollection。当前集合全部子步骤完成后,再处理下一个。
先判断是否需要迁移。运行 $collStats,投影 ns 和 shard:
db.<collName>.aggregate(
[
{
$collStats: { }
},
{
$project: {
ns: 1,
shard: 1
}
}
]
)
满足以下任一条件时,跳过当前集合,处理下一个:
ns已出现在第 4 步的$shardedDataDistribution输出中。shard不是正在移除的分片。
两项都不满足时,移动集合:
sh.moveCollection( "<namespace>.<collection>", "<ID of recipient shard>" )
注意:对已经分片的命名空间执行 moveCollection 会失败。如果收到这个错误,忽略并继续处理下一个集合。
对数据库中每个集合重复上述判断与迁移,再对其他数据库重复第 6 步。最后重新遍历检查,确认待排空分片中没有残留未分片集合。
7. 更改主分片
运行:
db.printShardingStatus()
在输出的 databases 部分检查 database.primary。如果主分片是正在移除的分片,必须将数据库主分片迁往其他分片。使用 movePrimary:
警告:之前未通过集合迁移步骤移走的集合,在 movePrimary 过程中不可用。
db.adminCommand(
{
movePrimary: <dbName>,
to: <shardName>
}
)
8. 检查迁移状态
在 admin 数据库再次运行 removeShard:
db.adminCommand( { removeShard: "<shardName>" } )
输出的 remaining 包含:
| 字段 | 说明 |
|---|---|
chunks |
分片当前剩余块数 |
dbs |
仍以该分片为主分片的数据库数量,对应数据库列在 dbsToMove 中 |
jumboChunks |
全部剩余块中,jumbo 块的数量 |
如果 jumboChunks 大于 0,先等到只剩 jumbo 块,再手动清除 jumbo 标志,排空过程才能完成。标志清除后,平衡器可以迁移这些块。详情参阅清除 jumbo 标志和范围迁移流程。
持续检查,直到剩余块数为 0:
db.adminCommand( { removeShard: "<shardName>" } )
9. 完成分片移除
再次运行 removeShard:
db.adminCommand( { removeShard: <shardName> } )
DDL 操作注意事项:如果集群同时执行修改集合的 DDL 操作,例如 reshardCollection,removeShard 会在并发 DDL 完成之后运行。
成功移除时,输出类似:
{
msg: 'removeshard completed successfully',
state: 'completed',
shard: '<shardName>',
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1721941519, i: 7 }),
signature: {
hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0),
keyId: Long('0')
}
},
operationTime: Timestamp({ t: 1721941519, i: 7 })
}
延伸阅读
- 为分片添加成员。
- 备份自管理集群元数据。
- 使用块进行数据分区。
原文:Remove Shards from a Sharded Cluster。作者/维护方:MongoDB 文档维护者。本文为中文翻译,代码及命令保留原文。











暂无评论内容