从 MongoDB 分片集群中移除分片

移除分片前,必须确保其数据已迁移到集群其余分片。本文介绍如何安全迁移数据并移除分片。

关于本任务

执行过程中创建集合、对集合分片或移动集合,可能造成中断及意外结果。

不要用此流程把整个集群迁移到新硬件,应使用自管理分片集群硬件迁移指南。

如果集群中的块分布不均,平衡器会先迁出待移除分片中的块,然后再平衡其余分布。

移除分片可能关闭已有变更流游标,且关闭的游标不一定能够完整恢复。

分片移除期间可以安全重启集群。正在进行的排空操作会在组件重启后自动继续,状态记录于 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 文档维护者。本文为中文翻译,代码及命令保留原文。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容