共计 7027 个字符,预计需要花费 18 分钟才能阅读完成。
作者:任坤
现居珠海,先后负责专职 Oracle 和 MySQL DBA,当初次要负责 MySQL、mongoDB 和 Redis 保护工作。
本文起源:原创投稿
* 爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。
1 背景
网上基于 mongo 单实例 PITR 的案例比拟多,官档也有对于 mongo cluser 的复原步骤,但于基于 mongo cluster 的 PITR 案例简直没有。本文基于试验环境,模仿线上环境实现一次针对 mongo cluster 的 PITR。
原集群实例散布状况:
172.16.129.170 shard1 27017 shard2 27018 config 37017 mongos 47017
172.16.129.171 shard1 27017 shard2 27018 config 37017 mongos 47017
172.16.129.172 shard1 27017 shard2 27018 config 37017 mongos 47017
思考到线上环境的理论数据会比拟大,这里只将 shard 复原成单实例,能供开发查问数据即可。复原后的实例散布状况:
172.16.129.173 shard1 27017 shard2 27018 config 37017 mongos 47017
172.16.129.174 config 37017
172.16.129.175 config 37017
mongo 版本为 percona 4.2.13,对每个 shard 实例和 config server 都部署 hot backup 定时备份脚本,同时对每个 shard 实例和 config server 部署 oplog 定时备份脚本。构建测试数据,通过 mongos 登录集群,创立一个 hash 分片表并插入 10 条数据:
use admin
db.runCommand({"enablesharding":"renkun"})
sh.shardCollection("renkun.user", { id: "hashed"} )
use renkun
var tmp = [];
for(var i =0; i<10; i++){tmp.push({ 'id':i, "name":"Kun" + i});
}
db.user.insertMany(tmp);
对 shard1、shard2 和 config server 执行物理热备,持续插入数据:
use renkun
var tmp = [];
for(var i =10; i<20; i++){tmp.push({ 'id':i, "name":"Kun" + i});
}
db.user.insertMany(tmp);
对 shard、shard2 和 config server 进行 oplog 备份,oplog 备份文件里蕴含了以上所有操作。此时 user 表有 20 条数据,并且 id 从 0 -20 顺次递增。咱们的要求是将集群复原到 max(id)=15 时的快照点。
官档给出的步骤是顺次复原出 config server、shard 和 mongo,然而官档上没有前滚 oplog 的操作。
咱们的案例中须要先从 shard 的 oplog 文件中解析出待复原的工夫点,因而调整一下程序,先复原 shard 再复原 config server。
2 复原 shard 单实例
将 shard 的物理备份和 oplog 备份传输到指标机器,物理备份间接解压到数据目录即可启动。
2.1 确认待复原快照点
对两个 shard 的 oplog 备份别离执行如下操作
bsondump oplog.rs.bson > oplog.rs.json
more oplog.rs.json | egrep "\"op\"\:\"\i\",\"ns\":\"renkun\.user\""| grep"\"Kun
15\""
在 shard2 的 oplog 备份上查问到
{"ts":{"$timestamp":{"t":1623408268,"i":6}},"t":{"$numberLong":"1"},"h":
{"$numberLong":"0"},"v":{"$numberInt":"2"},"op":"i","ns":"renkun.user","ui":{"$binary":
{"base64":"uhlG0e4sRB+RUfFOzpMCEQ==","subType":"04"}},"wall":{"$date":
{"$numberLong":"1623408268694"}},"o":{"_id":{"$oid":"60c33e8c1c3edd59f25eecb5"},"id":
{"$numberDouble":"15.0"},"name":"Kun 15"}}
由此确认 id=15 插入时对应的工夫戳为 1623408268:6,然而 mongorestore 的 –oplogLimit 指定的工夫戳参数为开区间,即不会重放指定工夫戳对应的 oplog entry,因而要想复原到这个工夫点就必须对其再加 1 位,于是变成了 1623408268:7。
2.2 创立长期账号
用 root 账号登录 shard 实例,创立 1 个具备 __system 角色的用户,命名为 internal_restore。这个用户不仅用于前滚 oplog,还用于批改 admin.system.version 以及删除 local db,root 账号默认没有权限执行这些操作。
use admin
db.createUser( { user: "internal_restore", pwd: "internal_restore", roles: ["__system"] })
用 internal_restore 登录两个 shard 实例,删除 local db。
use local
db.dropDatabase()
2.3 前滚 oplog 至指定工夫点
mongorestore ‐h 127.0.0.1 ‐uinternal_restore ‐p"internal_restore" ‐‐port 27017 ‐‐
oplogReplay ‐‐oplogLimit "1623408268:7" ‐‐authenticationDatabase admin
/data/backup/202106111849_27017/local/oplog.rs.bson
mongorestore ‐h 127.0.0.1 ‐uinternal_restore ‐p"internal_restore" ‐‐port 27018 ‐‐
oplogReplay ‐‐oplogLimit "1623408268:7" ‐‐authenticationDatabase admin
/data/backup/202106111850_27018/local/oplog.rs.bson
别离登录 shard1 和 shard2 查看数据:
‐‐shard1 27017
> db.user.find().sort({"id":1})
{"_id" : ObjectId("60c330322424943565780766"), "id" : 3, "name" : "Kun 3" }
{"_id" : ObjectId("60c330322424943565780769"), "id" : 6, "name" : "Kun 6" }
{"_id" : ObjectId("60c33032242494356578076b"), "id" : 8, "name" : "Kun 8" }
{"_id" : ObjectId("60c33e8c1c3edd59f25eecb1"), "id" : 11, "name" : "Kun 11" }
{"_id" : ObjectId("60c33e8c1c3edd59f25eecb2"), "id" : 12, "name" : "Kun 12" }
‐‐shard2 27018
> db.user.find().sort({"id":1})
{"_id" : ObjectId("60c330322424943565780763"), "id" : 0, "name" : "Kun 0" }
{"_id" : ObjectId("60c330322424943565780764"), "id" : 1, "name" : "Kun 1" }
{"_id" : ObjectId("60c330322424943565780765"), "id" : 2, "name" : "Kun 2" }
{"_id" : ObjectId("60c330322424943565780767"), "id" : 4, "name" : "Kun 4" }
{"_id" : ObjectId("60c330322424943565780768"), "id" : 5, "name" : "Kun 5" }
{"_id" : ObjectId("60c33032242494356578076a"), "id" : 7, "name" : "Kun 7" }
{"_id" : ObjectId("60c33032242494356578076c"), "id" : 9, "name" : "Kun 9" }
{"_id" : ObjectId("60c33e8c1c3edd59f25eecb0"), "id" : 10, "name" : "Kun 10" }
{"_id" : ObjectId("60c33e8c1c3edd59f25eecb3"), "id" : 13, "name" : "Kun 13" }
{"_id" : ObjectId("60c33e8c1c3edd59f25eecb4"), "id" : 14, "name" : "Kun 14" }
{"_id" : ObjectId("60c33e8c1c3edd59f25eecb5"), "id" : 15, "name" : "Kun 15" }
数据已复原实现,总共 16 条,max(id)=15
2.4 批改 admin.system.version
shard 由原来的 3 节点复制集变成了单实例,host ip 也产生了扭转,因而要批改对应的元数据信息。以 internal_restore 用户登录两个 shard 实例,别离执行:
use admin
db.system.version.deleteOne({ _id: "minOpTimeRecovery"} )
db.system.version.find({"_id" : "shardIdentity"} )
db.system.version.updateOne({ "_id" : "shardIdentity"},
{ $set :
{ "configsvrConnectionString" :
"configdb/172.16.129.173:37017,172.16.129.174:37017,172.16.129.175:37017"}
}
)
在被删除和批改之前,这两条记录别离存储的还是老的 config server 信息,详情如下:
{ "_id" : "shardIdentity", "shardName" : "repl", "clusterId" :
ObjectId("60c2c9b44497a4f2e02510fd"), "configsvrConnectionString" :
"configdb/172.16.129.170:37017,172.16.129.171:37017,172.16.129.172:37017" }
{ "_id" : "minOpTimeRecovery", "configsvrConnectionString" :
"configdb/172.16.129.170:37017,172.16.129.171:37017,172.16.129.172:37017", "minOpTime" :
{"ts" : Timestamp(1623383008, 6), "t" : NumberLong(1) }, "minOpTimeUpdaters" : 0,
"shardName" : "repl" }
至此 2 个 shard server 曾经实现复原,下一步是复原 config server。
3 复原 config server
将物理备份传输到指标机器,间接解压到数据目录即可用。首先以单实例身份启动 config server,用 root 账号登录后,创立 1 个具备__system 角色的用户 (同上)。
3.1 前滚 oplog
mongorestore ‐h 127.0.0.1 ‐uinternal_restore ‐p"internal_restore" ‐‐port 37017 ‐‐
oplogReplay ‐‐oplogLimit "1623408268:7" ‐‐authenticationDatabase admin
/data/backup/202106111850_37017/local/oplog.rs.bson
3.2 批改元数据
以 interal_restore 身份登录实例,批改集群的 shard 元数据信息,具体操作如下:
use local
db.dropDatabase()
use config
db.shards.find()
db.shards.updateOne({"_id" : "repl"}, {$set : { "host" : "172.16.129.173:27017"} })
db.shards.updateOne({"_id" : "repl2"}, {$set : { "host" : "172.16.129.173:27018"} })
db.shards.find()
3.3 启动集群
敞开 config server,以集群模式启动,对应的配置文件开启如下参数:
sharding:
clusterRole: configsvr
replication:
oplogSizeMB: 10240
replSetName: configdb
登录后执行
rs.initiate()
rs.add("172.16.129.174:37017")
rs.add("172.16.129.175:37017")
此时 config server 变成 1 个 3 节点集群,也复原结束。
4 配置 mongos
能够间接把原环境的 mongos 配置文件复制过去,只须要批改一下 sharding 和 bindIp 参数即可
sharding:
configDB: "configdb/172.16.129.173:37017,172.16.129.174:37017,172.16.129.175:37017"
net:
port: 47017
bindIp: 127.0.0.1,172.16.129.173
启动后登录 mongos 查问 user 表,总共 16 条记录,max(id)=15,合乎预期后果。
mongos> use renkun
switched to db renkun
mongos> db.user.find().sort({"id":1})
{"_id" : ObjectId("60c330322424943565780763"), "id" : 0, "name" : "Kun 0" }
{"_id" : ObjectId("60c330322424943565780764"), "id" : 1, "name" : "Kun 1" }
{"_id" : ObjectId("60c330322424943565780765"), "id" : 2, "name" : "Kun 2" }
{"_id" : ObjectId("60c330322424943565780766"), "id" : 3, "name" : "Kun 3" }
{"_id" : ObjectId("60c330322424943565780767"), "id" : 4, "name" : "Kun 4" }
{"_id" : ObjectId("60c330322424943565780768"), "id" : 5, "name" : "Kun 5" }
{"_id" : ObjectId("60c330322424943565780769"), "id" : 6, "name" : "Kun 6" }
{"_id" : ObjectId("60c33032242494356578076a"), "id" : 7, "name" : "Kun 7" }
{"_id" : ObjectId("60c33032242494356578076b"), "id" : 8, "name" : "Kun 8" }
{"_id" : ObjectId("60c33032242494356578076c"), "id" : 9, "name" : "Kun 9" }
{"_id" : ObjectId("60c33e8c1c3edd59f25eecb0"), "id" : 10, "name" : "Kun 10" }
{"_id" : ObjectId("60c33e8c1c3edd59f25eecb1"), "id" : 11, "name" : "Kun 11" }
{"_id" : ObjectId("60c33e8c1c3edd59f25eecb2"), "id" : 12, "name" : "Kun 12" }
{"_id" : ObjectId("60c33e8c1c3edd59f25eecb3"), "id" : 13, "name" : "Kun 13" }
{"_id" : ObjectId("60c33e8c1c3edd59f25eecb4"), "id" : 14, "name" : "Kun 14" }
{"_id" : ObjectId("60c33e8c1c3edd59f25eecb5"), "id" : 15, "name" : "Kun 15" }
至此,一个残缺的 mongo cluster pitr 操作正式实现。
5 总结
mongo 4.X 开始引入事务,尤其是 4.2 反对跨分片事务,依照官档介绍复原事务波及的数据必须要用指定工具,上述操作暂不实用。