vlambda博客
学习文章列表

[译文]还原MongoDB 分片集群到另一个环境


还原MongoDB 分片集群到另一个环境

如果您在数据库领域工作一段时间,您可能遇到需要基于现有的数据库,创建新的数据库的需求。最常见的例子我想是创建一个生产数据库的副本用于测试。对于MongoDB分片集群,官方文档介绍了从备份恢复分片集群的过程。但是,如果我们想要将数据还原到不同的主机,并重命名分片或者副本集,该如何处理?文档中提到了一些元数据重命名,但是这些步骤不完整。我将提供MongoDB4.2的详细步骤,但我应该告诉您使用它们的风险,因为这不是一个受支持的过程。我不会在这里详细介绍备份部分,但是通常我们可以停止平衡器,然后关闭每个分片的从节点,以及config server,并创建快照作为新集群的成员。您还可以查看Corrado的博客Percona Backup for MongoDB in Action(题外话:这个工具之前版本测试过,感觉很多问题,也希望有成功使用这个工具的大佬能够留言交流一波,虽然目前还是停留在单口相声被白嫖阶段>_<),以不停机的情况下来获得分片集群的一致性备份。

恢复MongoDB分片集群概述

在这个场景下,我假设我们要克隆一个集群,其中组件命名如下:

 
   
   
 
  1. # config servers

  2. prod-cfg/prod-cfg1.domain,prod-cfg2.domain,prod-cfg3.domain


  3. # shards

  4. prod-shard-01/prod-s01a.domain,prod-s01b.domain,prod-s01c.domain

  5. prod-shard-02/prod-s02a.domain,prod-s02b.domain,prod-s02c.domain

  6. ...

目标集群有以下命名:

 
   
   
 
  1. # config servers

  2. stage-cfg/stage-cfg1.domain,stage-cfg2.domain,stage-cfg3.domain


  3. # shards

  4. stage-shard-01/stage-s01a.domain,stage-s01b.domain,stage-s01c.domain

  5. stage-shard-02/stage-s02a.domain,stage-s02b.domain,stage-s02c.domain

  6. ...

这里需要注意的一点是,目标集群需要保持与源集群相同数量的分片。根据分片的数量,元数据编辑可能非常耗时,因此您应该使用某种自动化的方法。至少使用将终端复用,这样您可以将在单个选项卡中输出的命令广播到其他选项卡中(每个分片对应一个窗口)

还原Config Servers

首先您要做的是将config server恢复到新服务器上。一旦我们有了这些,遵循以下步骤:

  • 1.以独立模式启动config server。重命名config server中的配置文件,注释与复制集有关选项

 
   
   
 
  1. sed -i 's/sharding:/#sharding:/' /etc/mongod.conf

  2. sed -i 's/ clusterRole: /# clusterRole: /' /etc/mongod.conf

  3. sed -i 's/replication:/#replication:/' /etc/mongod.conf

  4. sed -i 's/ replSetName: prod-cfg/# replSetName: stage-cfg/' /etc/mongod.conf

 
   
   
 
  1. service mongod start

  • 2.删除 local数据库

 
   
   
 
  1. mongo -u$user -p$password --port=27019 <<EOF

  2. use local

  3. db.dropDatabase()

  4. EOF

  • 3.更新 config.shards集合的分片元数据

 
   
   
 
  1. use config

  2. db.shards.find({}).forEach(function(oldshard) {

  3. newshard=oldshard

  4. newshard["_id"]=oldshard["_id"].replace(/prod/gi,'stage');

  5. newshard["host"]=oldshard["host"].replace(/prod/gi,'stage');

  6. db.shards.save(newshard);

  7. db.shards.remove({_id: oldshard["_id"]});

  8. })

  • 4.对未分片的集合修改主分片的名称

 
   
   
 
  1. use config

  2. db.databases.find().forEach(function(shard) {

  3. shard["primary"]=shard["primary"].replace(/prod/gi,'stage');

  4. db.databases.save(shard);

  5. })

  • 5.修改每个chunk的元数据(如果您有很多chunk这可能会持续一段时间)

 
   
   
 
  1. db.chunks.find().forEach(function(shard) {

  2. shard["shard"]=shard["shard"].replace(/prod/gi,'stage');

  3. db.chunks.save(shard);

  4. })

  • 6.修改chunk的历史元数据

 
   
   
 
  1. db.chunks.find().forEach(function(shard) {

  2. shard["history"][0].shard=shard["history"][0].shard.replace(/prod/gi,'stage');

  3. db.chunks.save(shard);

  4. })

  • 7.以新名称启动config server并初始化副本集

 
   
   
 
  1. sed -i 's/#sharding:/sharding:/' /etc/mongod.conf

  2. sed -i 's/# clusterRole: / clusterRole: /' /etc/mongod.conf

  3. sed -i 's/#replication:/replication:/' /etc/mongod.conf

  4. sed -i 's/# replSetName:/ replSetName:/' /etc/mongod.conf

 
   
   
 
  1. service mongod restart

 
   
   
 
  1. mongo -u$user -p$password --port=27019

  2. rs.initiate(

  3. {

  4. _id: "stage-cfg",

  5. configsvr: true,

  6. members: [

  7. { _id : 0, host : "stage-cfg1.domain:27019" },

  8. ]

  9. }

  10. )

此时,可以安全地添加config server 副本集的其他成员。

 
   
   
 
  1. rs.add("stage-cfg2.domain:27019")

  2. rs.add("stage-cfg3.domain:27019")

接着,我们处理分片。

恢复分片

恢复每个分片的备份后,执行以下操作:

  • 1.以独立模式启动每个分片成员

 
   
   
 
  1. sed -i 's/sharding:/#sharding:/' /etc/mongod.conf

  2. sed -i 's/ clusterRole: /# clusterRole: /' /etc/mongod.conf

  3. sed -i 's/replication:/#replication:/' /etc/mongod.conf

  4. sed -i 's/ replSetName: prod-shard-/# replSetName: stage-shard-/' /etc/mongod.conf

 
   
   
 
  1. service mongod start

  • 2.创建一个具有 __system角色的临时用户。这用于编辑系统集合。

 
   
   
 
  1. mongo -u$user -p$password --port=27018 <<EOF

  2. db.createUser(

  3. {

  4. user: "mySystemUser",

  5. pwd: "percona",

  6. roles: [ "__system" ]

  7. }

  8. )

  9. EOF

  • 3.删除 local数据库

 
   
   
 
  1. db.auth("mySystemUser","percona")

  2. use local

  3. db.dropDatabase()

  • 4.删除存储Oplog还原信息的文档(我们想在启动时避免任何类型的还原,只需保持数据不变)

 
   
   
 
  1. use admin

  2. db.system.version.deleteOne( { _id: "minOpTimeRecovery" } )

  • 5.更新分片身份元数据

 
   
   
 
  1. db.system.version.find({"_id" : "shardIdentity"}).forEach(function(shard) {

  2. shard["configsvrConnectionString"]=shard["configsvrConnectionString"].replace(/prod/gi,'stage');

  3. shard["shardName"]=shard["shardName"].replace(/prod/gi,'stage');

  4. db.system.version.save(shard);

  5. })

  • 6.删除缓存的元数据集合的所有文档。基本上都是以 db.cache开头

 
   
   
 
  1. use config

  2. db.cache.chunks.config.system.sessions.remove({})

  3. db.cache.collections.remove({})

  4. db.cache.databases.remove({})

  5. db.cache.chunks["mydb.mycol1"].remove({})

  6. db.cache.chunks["mydb.mycol2"].remove({})

  7. ...

  • 7.重新以单节点副本集模式启动每个分片,并添加从节点。在本例中,我将它们添加为无投票权限的,因为我们不希望它们意外地成为主节点(至少在他们从具有实际数据的节点完成同步之前)

 
   
   
 
  1. sed -i 's/#sharding:/sharding:/' /etc/mongod.conf

  2. sed -i 's/# clusterRole: / clusterRole: /' /etc/mongod.conf

  3. sed -i 's/#replication:/replication:/' /etc/mongod.conf

  4. sed -i 's/# replSetName:/ replSetName:/' /etc/mongod.conf

 
   
   
 
  1. service mongod restart

 
   
   
 
  1. rs.initiate( { _id: "stage-s01", members: [ { _id : 0, host :"stage-s01a.domain:27018" }, ] })

  2. rs.add({host:"stage-s01b.domain:27018", priority: 0, votes: 0 })

  3. rs.add({host:"stage-s01c.domain:27018", priority: 0, votes: 0 })

  • 8.最后,不要忘记删除我们之前创建的有权限的用户。

 
   
   
 
  1. use admin

  2. db.removeUser("mySystemUser")

最后的话

虽然上面的步骤是有效的,但是如果MongoDB能有一些内置的脚本完成这个繁琐的任务就好了。希望未来的版本包括这些内容。如果您有机会使用这个方法,请在下面留下您的评论,让我知道它的进展。