MongoDB复制集环境基于时间点的恢复

背景

对于任何类型的数据库,如果要将数据库恢复到过去的任意时间点,需要有过去某个时间点的全备+全备之后的重做日志,MongoDB也不例外。使用全备将数据库恢复到固定时刻,然后使用重做日志追加全备之后的操作。

案例3: 误操作某个集合,对单个集合进行恢复

案例2: 误删除某个DB,对单个DB进行恢复

案例1:将整个实例恢复到某个时间点

写在前面

摘录自https://www.cnblogs.com/lijiaman/p/13526238.html(推荐阅读)
link

1.概述

MongoDB复制集环境基于时间点的恢复

重做日志备份:MongoDB只有开启主从复制或者副本集时才会开启重做日志,主从复制存放在local数据库下的oplog.$main集合中,复制集的日志存放在local数据库下的oplog.rs集合中,该集合是一个上限集合,当达到固定大小时,最老的记录会被自动覆盖。

因此需要注意,MongoDB的重做日志并不会一直保存着,能否恢复到故障点,完全取决于日志是否完整。

环境

MongoDB 4.2.8版本副本集环境(一主一从一仲裁)

确认日志保存情况

oplog是一个上限集合,当数据量达到一定大小后,MongoDB会自动清理oplog日志信息,为了保证恢复能够正常进行,需要确认日志的时间是否符合还原需求。

简单来说,oplog应该保存着自上一次备份以来的所有日志。可以使用下面方法来确认最早的oplog。

方法:查看主从复制信息 在主节点查看日志信息,可以看到oplog日志大小,因为oplog是一个固定大小的集合,所以还可以看到日志的开始、结束时间、oplog的时间差等。

rs0:PRIMARY>rs.printReplicationInfo()

configured oplog size:   990MB
log length start to end: 14775296secs (4104.25hrs)
oplog first event time:  Fri Jul 03 2020 07:23:41 GMT+0000 (UTC)
oplog last event time:   Mon Dec 21 2020 07:38:37 GMT+0000 (UTC)
now:                     Mon Dec 21 2020 07:38:44 GMT+0000 (UTC)

案例1:将整个实例恢复到某个时间点

场景描述

业务人员发现多个MongoDB数据库均存在数据错误的情况,需要将全部数据恢复到过去的某个时刻。

数据恢复方法描述
只要确定了恢复时间点,就可以使用完全备份+oplog备份,将数据恢复到过去的某个时刻。

大致流程,(恢复过程中需停库,并且是整个实例(所有库都受影响)) 1.基于全备(数据恢复到故障之前), 2.基于local库的oplog日志(通过数据的_id找到需要恢复的时间点)其中需要更改日志备份的文件名,并且保证日志没有被覆盖

恢复过程

1.模拟业务正常运行,数据正常进入MongoDB数据库

db.test1.insert({id:1,name:'a'})
db.test1.insert({id:2,name:'b'})

db.test1.find()
{ "_id" : ObjectId("5fe05220bfb5260f460a6eec"), "id" : 1, "name" : "a" }
{ "_id" : ObjectId("5fe05222bfb5260f460a6eed"), "id" : 2, "name" : "b" }

2.执行完整备份

mongodump --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" --readPreference=secondary  -o /dfile/mongobak

以上为Replica Set(副本集备份)
默认情况下,mongodump读取Primary节点的数据,如果想读取Secondary节点的数据,则需要使用选项readPreference。

rs0为副本集的名称 

-o /dfile/mongobak  备份目录(目录按照数据库名显示)
[root@0e9aebbbddf3 mongobak]# ls
11  admin  test  tt
[root@0e9aebbbddf3 mongobak]# ls test
test.bson           test1.metadata.json  user00.bson           user111.metadata.json
test.metadata.json  user.bson            user00.metadata.json  users.bson
test1.bson          user.metadata.json   user111.bson          users.metadata.json

MongoDB复制集环境基于时间点的恢复

3.再次模拟业务正常运行,数据正常进入MongoDB数据库

模拟全备之后的数据写入,也就是需要通过日志恢复的内容

db.test1.insert({id:3,name:'c'})

最终数据如下:

db.test1.find()
{ "_id" : ObjectId("5fe05220bfb5260f460a6eec"), "id" : 1, "name" : "a" }
{ "_id" : ObjectId("5fe05222bfb5260f460a6eed"), "id" : 2, "name" : "b" }
{ "_id" : ObjectId("5fe0565fbfb5260f460a6eee"), "id" : 3, "name" : "c" }

4.模拟数据误操作

test1集合id增加100
use test
db.test1.update({},{$inc:{"id":100}},{multi:true})

误操作之后的数据:

db.test1.find()
{ "_id" : ObjectId("5fe05220bfb5260f460a6eec"), "id" : 101, "name" : "a" }
{ "_id" : ObjectId("5fe05222bfb5260f460a6eed"), "id" : 102, "name" : "b" }
{ "_id" : ObjectId("5fe0565fbfb5260f460a6eee"), "id" : 103, "name" : "c" }

要求把所有数据库的数据恢复到STEP4之前的状态。

5.停止业务,不再往数据库写数据

6.备份日志。可以备份部分日志,也可以备份全部日志

mongodump --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" --readPreference=secondary  -d local -c oplog.rs -o /dfile/mongobak/oplog
mongodump --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" --readPreference=secondary  -d local -c oplog.rs -o /dfile/mongobak/oplog

2020-12-21T08:09:00.256+0000    WARNING: ignoring unsupported URI parameter 'replicaset'
2020-12-21T08:09:00.266+0000    writing local.oplog.rs to /dfile/mongobak/oplog/local/oplog.rs.bson
2020-12-21T08:09:00.271+0000    done dumping local.oplog.rs (1495 documents)

[mongobak]# ls oplog/
local
[mongobak]# ls oplog/local/
oplog.rs.bson  oplog.rs.metadata.json

7.确认数据异常时间点,对oplog集合进行分析

db.oplog.rs.find({$and:[{"ns":/test/},{"op":"u"}]}).sort({ts:1})db.oplog.rs.find({$and:[{"ns":/test/},{"op":"u"}]}).sort({ts:1})

查询结果如下,可以确认,开始对test.test1集合更新的时间为Timestamp(1608537904, 1)
#根据数据的_id和下面的_id对应

{ "ts" : Timestamp(1608537904, 1), "t" : NumberLong(11), "h" : NumberLong(0), "v" : 2, "op" : "u", "ns" : "test.test1", "ui" : UUID("02287b04-f11b-4069-8816-d87199bc9f5b"), "o2" : { "_id" : ObjectId("5fe05220bfb5260f460a6eec") }, "wall" : ISODate("2020-12-21T08:05:04.795Z"), "o" : { "$v" : 1, "$set" : { "id" : 101 } } }
{ "ts" : Timestamp(1608537904, 2), "t" : NumberLong(11), "h" : NumberLong(0), "v" : 2, "op" : "u", "ns" : "test.test1", "ui" : UUID("02287b04-f11b-4069-8816-d87199bc9f5b"), "o2" : { "_id" : ObjectId("5fe05222bfb5260f460a6eed") }, "wall" : ISODate("2020-12-21T08:05:04.796Z"), "o" : { "$v" : 1, "$set" : { "id" : 102 } } }
{ "ts" : Timestamp(1608537904, 3), "t" : NumberLong(11), "h" : NumberLong(0), "v" : 2, "op" : "u", "ns" : "test.test1", "ui" : UUID("02287b04-f11b-4069-8816-d87199bc9f5b"), "o2" : { "_id" : ObjectId("5fe0565fbfb5260f460a6eee") }, "wall" : ISODate("2020-12-21T08:05:04.796Z"), "o" : { "$v" : 1, "$set" : { "id" : 103 } } }

8.执行完全备份的恢复

``
需要注意,考虑是否需要使用"–drop"选项,如果不用该选项,会保留集合中当前的数据,如果使用了drop选项,在导入集合时会先删除集合。这里使用该选项(因为我们要修改,所以需要添加–drop)

如果还原导入已存在的实例中的数据库或集合有已存在相同_id的文档,mongorestore并不会覆盖。

``

mongorestore --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017"  --drop  /dfile/mongobak

注意执行之后的信息:
22 document(s) restored successfully. 0 document(s) failed to restore.

确认全量恢复的数据,已经恢复回来

#恢复之前
rs0:PRIMARY> use testdb.test1.find()db.test1.find()
{ "_id" : ObjectId("5fe05220bfb5260f460a6eec"), "id" : 101, "name" : "a" }
{ "_id" : ObjectId("5fe05222bfb5260f460a6eed"), "id" : 102, "name" : "b" }
{ "_id" : ObjectId("5fe0565fbfb5260f460a6eee"), "id" : 103, "name" : "c" }
#恢复之后
rs0:PRIMARY> db.test1.find()db.test1.find()
{ "_id" : ObjectId("5fe05220bfb5260f460a6eec"), "id" : 1, "name" : "a" }
{ "_id" : ObjectId("5fe05222bfb5260f460a6eed"), "id" : 2, "name" : "b" }

MongoDB复制集环境基于时间点的恢复

9.使用oplog执行增量恢复

在恢复oplog之前,需要对其格式进行处理,否则会报错

#报错信息
mongorestore --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" --oplogReplay --oplogLimit "1608537904:1"  /dfile/mongobak/oplog/local


2020-12-21T08:32:17.219+0000    WARNING: ignoring unsupported URI parameter 'replicaset'
2020-12-21T08:32:17.226+0000    preparing collections to restore from
2020-12-21T08:32:17.226+0000    don't know what to do with file "/dfile/mongobak/oplog/local/oplog.rs.bson", skipping...
2020-12-21T08:32:17.227+0000    don't know what to do with file "/dfile/mongobak/oplog/local/oplog.rs.metadata.json", skipping...
2020-12-21T08:32:17.227+0000    Failed: no oplog file to replay; make sure you run mongodump with --oplog
2020-12-21T08:32:17.227+0000    0 document(s) restored successfully. 0 document(s) failed to restore.

需要把oplog.rs.metadata.json 文件删除,把oplog.rs.bson名字改为oplog.bson

[root@0e9aebbbddf3 mongobak]# cd oplog/
[root@0e9aebbbddf3 oplog]# ls
local
[root@0e9aebbbddf3 oplog]# cd local/
[root@0e9aebbbddf3 local]# ls
oplog.rs.bson  oplog.rs.metadata.json
[root@0e9aebbbddf3 local]# pwd
/dfile/mongobak/oplog/local
[root@0e9aebbbddf3 local]# rm -rf oplog.rs.metadata.json 
[root@0e9aebbbddf3 local]# mv oplog.rs.bson oplog.bson    
[root@0e9aebbbddf3 local]# ls
oplog.bson

最后执行oplog增量恢复即可

#成功执行的信息
mongorestore --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" --oplogReplay --oplogLimit "1608537904:1"  /dfile/mongobak/oplog/local

2020-12-21T08:35:26.823+0000    WARNING: ignoring unsupported URI parameter 'replicaset'
2020-12-21T08:35:26.829+0000    preparing collections to restore from
2020-12-21T08:35:26.829+0000    replaying oplog
2020-12-21T08:35:26.961+0000    applied 101 oplog entries
2020-12-21T08:35:26.961+0000    0 document(s) restored successfully. 0 document(s) failed to restore

权限问题:原文提到恢复用户不能具有root的角色

10.确认数据恢复情况,发现数据以及恢复到了STEP4之前的状态

rs0:PRIMARY> db.test1.find()db.test1.find()
{ "_id" : ObjectId("5fe05220bfb5260f460a6eec"), "id" : 1, "name" : "a" }
{ "_id" : ObjectId("5fe05222bfb5260f460a6eed"), "id" : 2, "name" : "b" }
{ "_id" : ObjectId("5fe0565fbfb5260f460a6eee"), "id" : 3, "name" : "c" }

MongoDB复制集环境基于时间点的恢复
至此恢复结束。

案例2: 误删除某个DB,对单个DB进行恢复

通常,每个DB承载不同的业务,相互之间没有关系,如果出现故障,往往会表现在某个DB上,因此,如果出现故障,只对相应的DB进行恢复,那将减小对业务的影响。

场景:假设业务运行过程中,数据库db3被人误删除了,我们需要对db3进行恢复,并且不能影响到其它的DB业务。

推荐做法:

可以在当前实例上进行恢复,也可以新启动一个mongod实例,用于数据恢复,我们采用新的mongod实例来恢复数据。

恢复到测试库,确定数据没有问题,最后通过备份恢复的方式恢复到生成环境


1.首先新启动一个mongod实例;
2.将已有的完全备份恢复到新的实例上;
3.备份oplog,只备份db3的oplog,其它数据库的不备份;
4.使用oplog将数据库恢复到删除之前;
5.检查db3数据库的数据,确认是否恢复回来;
6.如果第5步没有问题,mongodump导出db3数据库,然后倒入到生产环境中。


1.模拟业务正常运行,数据正常进入MongoDB数据库

use db3
db.db3test.insert({id:111,name:'aaa'})
db.db3test.insert({id:222,name:'bbb'})
db.db3test.insert({id:333,name:'ccc'})

rs0:PRIMARY> db.db3test.find()db.db3test.find()
{ "_id" : ObjectId("5fe06487bfb5260f460a6eef"), "id" : 111, "name" : "aaa" }
{ "_id" : ObjectId("5fe06487bfb5260f460a6ef0"), "id" : 222, "name" : "bbb" }
{ "_id" : ObjectId("5fe06488bfb5260f460a6ef1"), "id" : 333, "name" : "ccc" }

2.执行完整备份

mongodump --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" --readPreference=secondary  -o /dfile/mongobak

3.再次模拟业务正常运行,数据正常进入MongoDB数据库

db.db3test.insert({id:444,name:'ddd'})
db.db3test.insert({id:555,name:'eee'})
db.db3test.insert({id:666,name:'fff'})

最终数据如下:

db.db3test.find()
{ "_id" : ObjectId("5fe06487bfb5260f460a6eef"), "id" : 111, "name" : "aaa" }
{ "_id" : ObjectId("5fe06487bfb5260f460a6ef0"), "id" : 222, "name" : "bbb" }
{ "_id" : ObjectId("5fe06488bfb5260f460a6ef1"), "id" : 333, "name" : "ccc" }
{ "_id" : ObjectId("5fe06563bfb5260f460a6ef2"), "id" : 444, "name" : "ddd" }
{ "_id" : ObjectId("5fe06563bfb5260f460a6ef3"), "id" : 555, "name" : "eee" }
{ "_id" : ObjectId("5fe06564bfb5260f460a6ef4"), "id" : 666, "name" : "fff" }

4.模拟数据误操作

rs0:PRIMARY> dbdb
db3
rs0:PRIMARY> db.dropDatabase()
        "dropped" : "db3",
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1608541699, 2),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1608541699, 2)
}

show tables;

接下来执行恢复操作。

5.在发现误操作之后,首先应该备份oplog,这里只涉及到db3数据库,只要备份db3的oplog即可,这样可以加快备份恢复速度

mongodump --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" --readPreference=secondary  -d local -c oplog.rs -q '{"ns":{"$regex":"db3"}}'  -o /dfile/mongobak/oplog

6.先执行完全恢复(只恢复db3库)

#使用的是全备目录中的db3目录(推荐先恢复到测试库)
mongorestore --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" -d db3  /dfile/mongobak/db3

执行恢复后,db3数据已经恢复到了全备时的状态

rs0:PRIMARY> show tables;
db3test
rs0:PRIMARY> show collections
db3test
rs0:PRIMARY> db.db3test.find()
{ "_id" : ObjectId("5fe06487bfb5260f460a6eef"), "id" : 111, "name" : "aaa" }
{ "_id" : ObjectId("5fe06487bfb5260f460a6ef0"), "id" : 222, "name" : "bbb" }
{ "_id" : ObjectId("5fe06488bfb5260f460a6ef1"), "id" : 333, "name" : "ccc" }

7.使用oplog恢复到drop操作之前

先确认drop db3数据库的时间点:Timestamp(1608541699, 2)

use local
db.oplog.rs.find(
 {
     $and : [
      {"ns" : {"$regex":"db3"}},
      {"op" : "c"},
      {"o"  : {"dropDatabase":1}}
     ]
  }
).sort({ts:1})
#结果如下
{ "ts" : Timestamp(1608541699, 2), "t" : NumberLong(11), "h" : NumberLong(0), "v" : 2, "op" : "c", "ns" : "db3.$cmd", "wall" : ISODate("2020-12-21T09:08:19.243Z"), "o" : { "dropDatabase" : 1 } }
#Timestamp(1608541699, 2)的值和删除时显示的一致

执行增量恢复:

先处理oplog,删除文件oplog.rs.metadata.json,修改oplog.rs.bson为oplog.bson

[root@0e9aebbbddf3 oplog]# cd local/
[root@0e9aebbbddf3 local]# ls
oplog.bson  oplog.rs.bson  oplog.rs.metadata.json
[root@0e9aebbbddf3 local]# rm -rf oplog.rs.metadata.json 
[root@0e9aebbbddf3 local]# mv oplog. 
oplog.bson     oplog.rs.bson  
[root@0e9aebbbddf3 local]# mv oplog.rs.bson oplog.bson 
mv: overwrite 'oplog.bson'? y  #上次遗留的(可覆盖,可以移动到其他目录)
[root@0e9aebbbddf3 local]# ls
oplog.bson

执行恢复
--oplogLimit "1608541699:1" 是在use local db.oplog.rs.find()中查到的前一个时间点(因为--oplogLimit "1608541699:2",是查到的但是包含了drop database操作)。需要手动减1

mongorestore --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" --oplogReplay --oplogLimit "1608541699:1"  /dfile/mongobak/oplog/local 
2020-12-21T09:29:53.276+0000    WARNING: ignoring unsupported URI parameter 'replicaset'
2020-12-21T09:29:53.285+0000    preparing collections to restore from
2020-12-21T09:29:53.285+0000    replaying oplog
2020-12-21T09:29:53.298+0000    applied 7 oplog entries
2020-12-21T09:29:53.298+0000    0 document(s) restored successfully. 0 document(s) failed to restore.

原文提示: 特别注意:这里发现,oplogLimit参数,在MongoDB 2.7版本中不包含后面的时间点,在4.2里面包含后面的时间点。如果我使用时间Timestamp(1597374734, 2)来作为恢复点,则发现数据库恢复完成又被删除了。
检查数据是否已经恢复,可以确认,数据已经恢复回来

db.db3test.find()
{ "_id" : ObjectId("5fe06487bfb5260f460a6eef"), "id" : 111, "name" : "aaa" }
{ "_id" : ObjectId("5fe06487bfb5260f460a6ef0"), "id" : 222, "name" : "bbb" }
{ "_id" : ObjectId("5fe06488bfb5260f460a6ef1"), "id" : 333, "name" : "ccc" }
{ "_id" : ObjectId("5fe06563bfb5260f460a6ef2"), "id" : 444, "name" : "ddd" }
{ "_id" : ObjectId("5fe06563bfb5260f460a6ef3"), "id" : 555, "name" : "eee" }
{ "_id" : ObjectId("5fe06564bfb5260f460a6ef4"), "id" : 666, "name" : "fff" }

至此恢复结束。

案例3: 误操作某个集合,对单个集合进行恢复

场景

业务人员执行误删某个文档(集合)

T1~T2:业务正常运行,数据正常进入数据库
T2:使用mongodump执行数据库完全备份
T2~T4:业务正常运行,数据正常进入数据库
T4:用户误删除数据
T4~T6:业务还在运行,但是已经出现问题,如此时还能正常插入数据,但是查询、更新、删除数据存在找不到数据的错误
T6:DBA需数据恢复

推荐做法:在测试上恢复,确认数据正确之后,通过备份恢复的方式恢复到生产中

可以在当前实例上进行恢复,也可以新启动一个mongod实例,用于数据恢复,我们在上一个例子中已经使用新建mongod实例的方式来恢复数据,本次实验我们直接在生产实例上进行恢复。


1.执行完全恢复,使用完全备份,将数据库恢复到T2时刻;
2.找到T4时刻故障之前的时间,从而确定T2~T4之间的oplog日志。结合T2时刻的全备+ T2~T4之间的oplog日志,实现数据恢复;(备注:这里不需要去确认T2之后的日志开始时间,在使用oplog恢复数据时,是通过唯一编号“_id”来操作数据的,oplog可能从全备份之前的任意时间开始,但是并不影响数据的正确性)。
3.找到T4时刻故障之后的时间,备份oplog。
4.使用oplog,实现T4~T6时间段的恢复。


恢复过程

1.模拟业务正常运行,数据正常进入MongoDB数据库

use db4
db.db4test.insert({id:1111,name:'aaaa'})
db.db4test.insert({id:2222,name:'bbbb'})
db.db4test.insert({id:3333,name:'cccc'})

2.执行完整备份

mongodump --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" --readPreference=secondary  -o /dfile/mongobak

3.再次模拟业务正常运行,数据正常进入MongoDB数据库

use db4
db.db4test.insert({id:4444,name:'dddd'})
db.db4test.insert({id:5555,name:'eeee'})
db.db4test.insert({id:6666,name:'ffff'})

最终数据如下:

db.db4test.find()
{ "_id" : ObjectId("5fe070fdbfb5260f460a6ef5"), "id" : 1111, "name" : "aaaa" }
{ "_id" : ObjectId("5fe070fdbfb5260f460a6ef6"), "id" : 2222, "name" : "bbbb" }
{ "_id" : ObjectId("5fe070ffbfb5260f460a6ef7"), "id" : 3333, "name" : "cccc" }
{ "_id" : ObjectId("5fe07227bfb5260f460a6ef8"), "id" : 4444, "name" : "dddd" }
{ "_id" : ObjectId("5fe07227bfb5260f460a6ef9"), "id" : 5555, "name" : "eeee" }
{ "_id" : ObjectId("5fe07228bfb5260f460a6efa"), "id" : 6666, "name" : "ffff" }

4.模拟数据误操作,删除2条数据

db.db4test.remove({id:{$gt:4444}})
WriteResult({ "nRemoved" : 2 })
rs0:PRIMARY> db.db4test.find()db.db4test.find()
{ "_id" : ObjectId("5fe070fdbfb5260f460a6ef5"), "id" : 1111, "name" : "aaaa" }
{ "_id" : ObjectId("5fe070fdbfb5260f460a6ef6"), "id" : 2222, "name" : "bbbb" }
{ "_id" : ObjectId("5fe070ffbfb5260f460a6ef7"), "id" : 3333, "name" : "cccc" }
{ "_id" : ObjectId("5fe07227bfb5260f460a6ef8"), "id" : 4444, "name" : "dddd" }

5.再次模拟业务正常运行,数据正常进入MongoDB数据库

use db4
db.db4test.insert({id:7777,name:'gggg'})
db.db4test.insert({id:8888,name:'hhhh'})
db.db4test.insert({id:9999,name:'kkkk'})

最终数据如下:

db.db4test.find()
{ "_id" : ObjectId("5fe070fdbfb5260f460a6ef5"), "id" : 1111, "name" : "aaaa" }
{ "_id" : ObjectId("5fe070fdbfb5260f460a6ef6"), "id" : 2222, "name" : "bbbb" }
{ "_id" : ObjectId("5fe070ffbfb5260f460a6ef7"), "id" : 3333, "name" : "cccc" }
{ "_id" : ObjectId("5fe07227bfb5260f460a6ef8"), "id" : 4444, "name" : "dddd" }
{ "_id" : ObjectId("5fe072c5bfb5260f460a6efb"), "id" : 7777, "name" : "gggg" }
{ "_id" : ObjectId("5fe072c6bfb5260f460a6efc"), "id" : 8888, "name" : "hhhh" }
{ "_id" : ObjectId("5fe072c7bfb5260f460a6efd"), "id" : 9999, "name" : "kkkk" }

此时,我们发现id为5555和6666的数据是被误删除的,需要恢复回来,并且要保留执行删除命令之后的数据。

6.在发现误操作之后,首先应该备份oplog,这里只涉及到db4.db4test集合,只要备份该集合的oplog即可,这样可以加快备份恢复速度

mongodump --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" --readPreference=secondary -d local -c 'oplog.rs' -q '{"ns":"db4.db4test"}'  -o /dfile/mongobak/oplog

7.对该集合执行完全恢复操作

mongorestore --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" -d db4 -c db4test  /dfile/mongobak/db4/db4test.bson

2020-12-21T10:08:23.286+0000    WARNING: ignoring unsupported URI parameter 'replicaset'
2020-12-21T10:08:23.293+0000    checking for collection data in /dfile/mongobak/db4/db4test.bson
2020-12-21T10:08:23.294+0000    restoring to existing collection db4.db4test without dropping
2020-12-21T10:08:23.294+0000    reading metadata for db4.db4test from /dfile/mongobak/db4/db4test.metadata.json
2020-12-21T10:08:23.294+0000    restoring db4.db4test from /dfile/mongobak/db4/db4test.bson
2020-12-21T10:08:23.299+0000    continuing through error: E11000 duplicate key error collection: db4.db4test index: _id_ dup key: { _id: ObjectId('5fe070fdbfb5260f460a6ef6') }
2020-12-21T10:08:23.299+0000    continuing through error: E11000 duplicate key error collection: db4.db4test index: _id_ dup key: { _id: ObjectId('5fe070ffbfb5260f460a6ef7') }
2020-12-21T10:08:23.300+0000    continuing through error: E11000 duplicate key error collection: db4.db4test index: _id_ dup key: { _id: ObjectId('5fe070fdbfb5260f460a6ef5') }
2020-12-21T10:08:23.359+0000    no indexes to restore
2020-12-21T10:08:23.359+0000    finished restoring db4.db4test (0 documents, 3 failures)
2020-12-21T10:08:23.359+0000    0 document(s) restored successfully. 3 document(s) failed to restore.

8.使用oplog,对该集合执行增量恢复操作

先查看对db4.db4test集合执行删除的开始时间Timestamp(1608544902, 1)

use local
db.oplog.rs.find(
 {
     $and : [
      {"ns" : /db4.db4test/},
      {"op" : "d" }
     ]
  }
).sort({ts:1})

{ "ts" : Timestamp(1608544902, 1), "t" : NumberLong(11), "h" : NumberLong(0), "v" : 2, "op" : "d", "ns" : "db4.db4test", "ui" : UUID("16ea0367-1ae2-4929-aad4-1dc16474d419"), "wall" : ISODate("2020-12-21T10:01:42.057Z"), "o" : { "_id" : ObjectId("5fe07227bfb5260f460a6ef9") } }
{ "ts" : Timestamp(1608544902, 2), "t" : NumberLong(11), "h" : NumberLong(0), "v" : 2, "op" : "d", "ns" : "db4.db4test", "ui" : UUID("16ea0367-1ae2-4929-aad4-1dc16474d419"), "wall" : ISODate("2020-12-21T10:01:42.058Z"), "o" : { "_id" : ObjectId("5fe07228bfb5260f460a6efa") } }

执行增量恢复:

先处理oplog,删除文件oplog.rs.metadata.json,修改oplog.rs.bson为oplog.bson

[root@0e9aebbbddf3 local]# ls
oplog.bson  oplog.rs.bson  oplog.rs.metadata.json
[root@0e9aebbbddf3 local]# rm -rf oplog.rs.metadata.json 
[root@0e9aebbbddf3 local]# mv oplog.rs.bson oplog.bson 
mv: overwrite 'oplog.bson'? y
[root@0e9aebbbddf3 local]# ls
oplog.bson

执行恢复
--oplogLimit "1608544902:1就是看到的第一个remove操作值

mongorestore --host="rs0/192.168.68.60:27017,192.168.68.61:27017,192.168.68.62:27017" --oplogReplay --oplogLimit "1608544902:1"  /dfile/mongobak/oplog/local


2020-12-21T10:18:08.274+0000    WARNING: ignoring unsupported URI parameter 'replicaset'
2020-12-21T10:18:08.283+0000    preparing collections to restore from
2020-12-21T10:18:08.283+0000    replaying oplog
2020-12-21T10:18:08.290+0000    applied 6 oplog entries
2020-12-21T10:18:08.290+0000    0 document(s) restored successfully. 0 document(s) failed to restore.

9.查看数据是否恢复,确认已经完全恢复回来

最后两行就是

db.db4test.find()
{ "_id" : ObjectId("5fe070fdbfb5260f460a6ef5"), "id" : 1111, "name" : "aaaa" }
{ "_id" : ObjectId("5fe070fdbfb5260f460a6ef6"), "id" : 2222, "name" : "bbbb" }
{ "_id" : ObjectId("5fe070ffbfb5260f460a6ef7"), "id" : 3333, "name" : "cccc" }
{ "_id" : ObjectId("5fe07227bfb5260f460a6ef8"), "id" : 4444, "name" : "dddd" }
{ "_id" : ObjectId("5fe072c5bfb5260f460a6efb"), "id" : 7777, "name" : "gggg" }
{ "_id" : ObjectId("5fe072c6bfb5260f460a6efc"), "id" : 8888, "name" : "hhhh" }
{ "_id" : ObjectId("5fe072c7bfb5260f460a6efd"), "id" : 9999, "name" : "kkkk" }
{ "_id" : ObjectId("5fe07227bfb5260f460a6ef9"), "id" : 5555, "name" : "eeee" }
{ "_id" : ObjectId("5fe07228bfb5260f460a6efa"), "id" : 6666, "name" : "ffff" }

至此恢复结束。

再次感谢原文
https://www.cnblogs.com/lijiaman/p/13526238.html
link

本文说明,主要技术内容来自互联网技术大佬的分享,还有一些自我的加工(仅仅起到注释说明的作用)。如有相关疑问,请留言,将确认之后,执行侵权必删

上一篇:爬虫(八):分析Ajax请求抓取今日头条街拍美图


下一篇:Ubuntu搭建简单的web服务器