1.MongoDB 2.7主从复制(master –> slave)环境基于时间点的恢复

Snipaste_2020-08-17_18-37-33

 

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

clipboard

重做日志备份:MongoDB只有开启主从复制或者副本集时才会开启重做日志,主从复制存放在local数据库下的oplog.$main集合中,复制集的日志存放在local数据库下的oplog.rs集合中,该集合是一个上限集合,当达到固定大小时,最老的记录会被自动覆盖。因此需要注意,MongoDB的重做日志并不会一直保存着,能否恢复到故障点,完全取决于日志是否完整。

 

(二)操作日志oplog
(2.1)oplog日志格式解析
为了查看oplog日志保存了什么信息,向test集合中插入2条数据:

db.test.insert({"empno":1,enamelijiaman",1)">age22,1)">addressyunnan,kungming"});
db.test.insert({2,1)">aaa18,1)">sichuan,chengdu"});

查看test集合的数据信息

db.test.find()
 /* 1 */
 {
     _id" : ObjectId(5f30eb58bcefe5270574cd54)," : 1.0,1)">22.0
 }

 2 5f30eb58bcefe5270574cd552.018.0
 }

使用下面查询语句查看oplog日志信息:

use local
var since = Math.floor(ISODate(2020-08-10T14:00:00.000Z").getTime() / 1000) - 8*60*60
var until = Math.floor(ISODate(2020-08-10T23:00:00.000Z
db.oplog.$main.(
 {
      $and : [
       {ns" : /lijiamandb.test/},{ts" : { $gt" : Timestamp(since,1)">1),1)">$lt":Timestamp(until,1)">1)}}
      ]
   }
 ).sort({ts:1})

结果如下:

" : Timestamp(1597070283,1)">opilijiamandb.testo : { } } 2 } }

oplog中各个字段的含义:
ts:数据写的时间,括号里面第1位数据代表时间戳,是自unix纪元以来的秒值,第2位代表在1s内订购时间戳的序列数
op:操作类型,可选参数有:
       -- "i": insert
       --"u": update
       --"d": delete
       --"c": db cmd
       --"db":声明当前数据库 (其中ns 被设置成为=>数据库名称+ '.')
       --"n": no op,即空操作,其会定期执行以确保时效性
ns:命名空间,通常是具体的集合
o:具体的写入信息
o2: 在执行更新操作时的where条件,仅限于update时才有该属性

因此,如果要实现MongoDB基于时间点的恢复,只要解析oplog日志,就可以实现操作重做。

 

(2.2)确认日志保存情况
oplog是一个上限集合,当数据量达到一定大小后,MongoDB会自动清理oplog日志信息,为了保证恢复能够正常进行,需要确认日志的时间是否符合还原需求。简单来说,oplog应该保存着自上一次备份以来的所有日志。可以使用下面2种方法来确认最早的oplog。
方法一:查询oplog中的最小时间

db.oplog.$main.aggregate([{$group:{_id:$ts}}}])


{
    min_salary1595503517,1)">)
}


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

> db.printReplicationInfo()
configured oplog size:   2129.547656059265MB
 log length start to end: 9180secs (.55hrs)
 oplog first event time:  Thu Jun 18 2020 21:43:14 GMT+0800 (CST)
 oplog last event time:   Fri Jun 19 00:16: (CST)
 now:                     Mon Aug 10 18:59:23 GMT+0800 (CST)


(2.3)备份oplog日志
在使用mongodump备份数据库时,默认是不备份oplog的,需要我们手动去备份,常用的备份方法如下。
(1)备份所有数据库的oplog日志

mongodump --authenticationDatabase admin -uroot -p123456 --db=local --collection='oplog.$main' --out=/root/backup/oplog


(2)备份单个数据库的oplog日志。例如,备份catdb数据库的oplog日志

' --query={"ns":/catdb/}' --out=/root/backup/oplog


(3)备份单个集合的oplog日志。例如,备份catdb.myc1集合的oplog日志

{"ns":"catdb.myc1"}' --out=/root/backup/oplog


(4)使用多个条件来过滤oplog日志

# 备份catdb数据库,且只备份insert操作的oplog日志 mongodump --authenticationDatabase admin -uroot -p123456 --db=local --collection={"ns":/catdb/,"op":"i"}' --out=/root/backup/oplog # 备份catdb数据库,且备份在时间Timestamp( 1597241858,1 )到 Timestamp( 1597242471,1 ) 之间的数据 # 需要注意,不包含上下限时间

mongodump --authenticationDatabase admin -uroot -p123456 --db=local --collection=' --out=/root/backup/oplog

 

(三)模拟将MongoDB恢复到任意时间点
(3.1)案例一:将整个实例恢复到某个时间点
(3.3.1)故障场景描述
业务人员发现多个MongoDB数据库均存在数据错误的情况,需要将全部数据恢复到过去的某个时刻。

 

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

 

(3.3.3)恢复过程
STEP1:模拟业务正常运行,数据正常进入MongoDB数据库

use db1 db.db1test.insert({id:a'}) db.db1test.insert({b}) use db2 db.db2test.insert({11,1)">aa}) db.db2test.insert({bb'})

STEP2:执行完整备份

mongodump --authenticationDatabase admin -uroot -p123456 -o /root/backup/full

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

3,1)">c33,1)">cc'})

最终数据如下:

use db1 switched to db db1 > db.db1test.() { 5f35110ba27e9a00c0f26862"),1)">idname } { 5f35110ba27e9a00c0f268635f35113ca27e9a00c0f26866 } > > > use db2 switched to db db2 > db.db2test.5f35110ba27e9a00c0f268645f35110ca27e9a00c0f268655f35113da27e9a00c0f26867 } >

STEP4:模拟数据误操作

# db1的db1test集合id增加100 use db1 db.db1test.update({},{$inc:{100}},{multi:true}) # db2的db2test集合被删除 use db2 db.db2test.drop()

错误操作之后的结果:

101,1)">102,1)">103,1)"> } > >() >

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

STEP5:停止业务,不再往数据库写数据

STEP6:备份日志。可以备份部分日志,也可以备份全部日志

mongodump --authenticationDatabase admin -uroot -p123456 -d local -c ' -o /root/backup/oplog/

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

use local db.oplog.$main.( { $and : [ {" : /db1/u } ] } ).1})

查询结果如下,可以确认,开始对db1.db1test集合更新的时间为Timestamp(1597313442,1)

{ 1597313442,1)">db1.db1testo2) },1)">$set : { 101.0 } } } 102.0 3 3103.0 } } }


STEP8:执行完全备份的恢复
需要注意,考虑是否需要使用"--drop"选项,如果不用该选项,会保留集合中当前的数据,如果使用了drop选项,在导入集合时会先删除集合。这里使用该选项

mongorestore --authenticationDatabase admin -uroot -p123456 --port=27017 --drop  /root/backup/full/

需要注意权限问题,这里发现使用root账号无法执行恢复,但是使用权限较小的root2账号却可以(备注:关于root和root2用户权限信息,会在文档结尾给出):
[root@mongo1 oplog]# mongorestore --authenticationDatabase admin -uroot -p123456 --port=27017 --drop  /root/backup/full/
connected to: 127.0.0.1:27017
2020-08-13T10:25:05.963+0000     going into namespace [admin.system.version]
1 document found
2020-08-13T10:25:05.964+0000     Creating index: { key: { _id: 1 },name: "_id_",ns: "admin.system.version" }
Error creating index admin.system.version: 13 err: "not authorized to create index on admin.system.version"
Aborted

[root@mongo1 full]# mongorestore --authenticationDatabase admin -uroot2 -p123456 --port=27017 --drop  /root/backup/full/

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

use db1 switched to db db1 > db.db1test. } > > > >STEP9:使用oplog执行增量恢复
在恢复oplog之前,需要对其格式进行处理,否则会报错:

# 报错提示找不到oplog [root@mongo1 full]# mongorestore --authenticationDatabase admin -uroot -p123456 --port=27017 --oplogReplay --oplogLimit 1597313442:1" /root/backup/oplog/local connected to: 127.0.0.1:27017 No oplog file to replay. Make sure you run mongodump with --oplog.

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

[root@mongo1 local]# pwd
/root/backup/oplog/local
[root@mongo1 local]# ls
oplog.$main.bson  oplog.$main.metadata.json
[root@mongo1 local]# rm -rf oplog.\$main.metadata.json 
[root@mongo1 local]# mv oplog.\$main.bson oplog.bson 
[root@mongo1 local]# 
oplog.bson

最后执行oplog增量恢复即可

" /root/backup/oplog/local

注意:这里有一个大坑,需要特别留意,在使用上述命令导入数据时,整个过程没有报错,但是最终数据并没有恢复回来,如下面所示:
整个导入过程没有报错

[root@mongo1 local]# mongorestore --authenticationDatabase admin -uroot -p123456 --port=27017
 2020-08-13T10:33:27.830+0000      Replaying oplog
 30.013+0000         Progress: 3055430/1353998783    0%    (bytes)
 33.005+5632309/36.003+8604531/    (bytes)
 ...
 ...
 44:07.009+1340889939/99%10.004+1351348749/4604171 documents found
 10.699+0000 Applied 4592833 oplog entries out of 4592837 (4 skipped).

然而数据未恢复回来

" }

查询error log日志,发现root用户没有权限执行导入

10.692+0000 [conn18] Unauthorized not authorized on db1 to execute command { applyOps: [ { ts: Timestamp 1597313291000|'),id: 1.0,name:  } } ] }
 2.0,1)">0000 [conn18] Unauthorized not authorized on db2 to execute command { applyOps: [ { ts: Timestamp db2.db2test11.0,1)">1597313292000|22.0,1)">1597313340000|3.0,1)">1597313341000|33.0,1)">" } } ] }

处理办法:使用root2用户导入

[root@mongo1 local]# mongorestore --authenticationDatabase admin -uroot2 -p123456 --port=" /root/backup/oplog/local

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

} >

至此恢复结束。

 


(3.2)案例二:误删除某个DB,对单个DB进行恢复
通常,每个DB承载不同的业务,相互之间没有关系,如果出现故障,往往会表现在某个DB上,因此,如果出现故障,只对相应的DB进行恢复,那将减小对业务的影响。
(3.2.1)故障场景描述
假设业务运行过程中,数据库db3被人误删除了,我们需要对db3进行恢复,并且不能影响到其它的DB业务。

 

(3.2.2)数据恢复方法描述
可以在当前实例上进行恢复,也可以新启动一个mongod实例,用于数据恢复,然后再把确认无误的数据导入到生产环境中,我们采用新的mongod实例来恢复数据。
1.首先新启动一个mongod实例;
2.将已有的完全备份恢复到新的实例上;
3.备份oplog,只备份db3的oplog,其它数据库的不备份;
4.使用oplog将数据库恢复到删除之前;
5.检查db3数据库的数据,确认是否恢复回来;
6.如果第5步没有问题,mongodump导出db3数据库,然后倒入到生产环境中。

 

(3.2.3)恢复过程
use db3 db.db3test.insert({111,1)">}) db.db3test.insert({222,1)">bbb333,1)">cccSTEP3:再次模拟业务正常运行,数据正常进入MongoDB数据库

444,1)">ddd555,1)">eee666,1)">fff
> db.db3test.5f352400a27e9a00c0f2686b5f352400a27e9a00c0f2686c5f352401a27e9a00c0f2686d5f352428a27e9a00c0f2686e5f352428a27e9a00c0f2686f5f352429a27e9a00c0f26870" }

STEP4:模拟数据误操作

db db3 > db.dropDatabase() { droppeddb3ok1 }

接下来执行恢复操作。

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

' -q {"ns":/db3/}' -o /root/backup/oplog/

STEP6:重新开启一个mongod实例

mongod --port=27018 --dbpath=/tmp/data

STEP7:在新的实例上恢复全备数据,只要恢复db3即可

mongorestore  --port=27018 -d db3 /root/backup/full/db3

确认数据全备恢复情况

# 执行恢复前 > show dbs admin (empty) local 0.078GB > # 执行恢复后,db3数据已经恢复到了全备时的状态 > show dbs admin (empty) db3 .078GB local .078GB > > > use db3 switched to db db3 > show collections db3test system.indexes > db.db3test.STEP8:在新的实例上恢复oplog数据,恢复到drop操作之前
先确认drop db3数据库的时间点:  "ts" : Timestamp(1597318247,1)

" : /db3/}) // 结果 { 1597318247,1)">db3.$cmddropDatabase } }

执行增量恢复:

# 先处理oplog,删除文件oplog.$main.metadata.json,修改oplog.$main.bson为oplog.bson [root@mongo1 local]# pwd /root/backup/oplog/local [root@mongo1 local]# f oplog.\$main.metadata.json [root@mongo1 local]# oplog.\$main.bson oplog.bson [root@mongo1 local]# oplog.bson # 执行恢复 mongorestore --port=27018 --oplogReplay --oplogLimit 1597318247:1" /root/backup/oplog/local

检查数据是否已经恢复,可以确认,数据已经恢复回来

STEP9:把数据导出再导入到生产环境

# 从新的mongod环境导出db3数据库 [root@mongo1 ~]# mongodump -d db3 --port=27018 -out=/root # 将db3导入到生产环境,这里需要考虑是否用--drop关键字

[root@mongo1 ~]# mongorestore --authenticationDatabase admin -uroot -p123456 --port=27017 -d db3 /root/db3

确认数据是否已经导入到生产环境:

show dbs admin .078GB catdb .078GB db1 .078GB db2 .078GB db3 .078GB dogdb .078GB lijiamandb .078GB local 4.076GB mydb .078GB testdb .078GB > > show collections db3test system.indexes > > db.db3test. } >

数据以及全部导入到了生产环境,测试完成。注意,别忘记关闭新建的mongod实例。

 

 


(3.3)案例三:误操作某个集合,对单个集合进行恢复
(3.3.1)故障场景描述
业务人员执行误删操DBA对数据进行恢复,详细过程如下:
T1~T2:业务正常运行,数据正常进入数据库
T2:使用mongodump执行数据库完全备份
T2~T4:业务正常运行,数据正常进入数据库
T4:用户误删除数据
T4~T6:业务还在运行,但是已经出现问题,如此时还能正常插入数据,但是查询、更新、删除数据存在找不到数据的错误
T6:DBA介入数据恢复

clipboard


(3.3.2)数据恢复方法描述
可以在当前实例上进行恢复,也可以新启动一个mongod实例,用于数据恢复,我们在上一个例子中已经使用新建mongod实例的方式来恢复数据,本次实验我们直接在生产实例上进行恢复。
1.执行完全恢复,使用完全备份,将数据库恢复到T2时刻;
2.找到T4时刻故障之前的时间,从而确定T2~T4之间的oplog日志。结合T2时刻的全备+ T2~T4之间的oplog日志,实现数据恢复;(备注:这里不需要去确认T2之后的日志开始时间,在使用oplog恢复数据时,是通过唯一编号“_id”来操作数据的,oplog可能从全备份之前的任意时间开始,但是并不影响数据的正确性)。
3.找到T4时刻故障之后的时间,备份oplog。
4.使用oplog,实现T4~T6时间段的恢复。

 

(3.3.3)恢复过程
use db4 db.db4test.insert({1111,1)">aaaa}) db.db4test.insert({2222,1)">bbbb3333,1)">ccccSTEP2:执行完整备份

4444,1)">dddd5555,1)">eeee6666,1)">ffff
> db.db4test.()
{ 5f3545c3a27e9a00c0f26871 }
{ 5f3545c3a27e9a00c0f268725f3545c4a27e9a00c0f268735f354631a27e9a00c0f268745f354631a27e9a00c0f268755f354632a27e9a00c0f26876STEP4:模拟数据误操作,删除2条数据

> db.db4test.remove({id:{$gt:4444}})
 WriteResult({ nRemoved })
 > 
 > db.db4test.STEP5:再次模拟业务正常运行,数据正常进入MongoDB数据库

7777,1)">gggg8888,1)">hhhh9999,1)">kkkk5f3546ada27e9a00c0f268775f3546ada27e9a00c0f268785f3546ada27e9a00c0f26879" }

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

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

{"ns":"db4.db4test"}STEP7:对该集合执行完全恢复操作

27017 -d db4 -c db4test /root/backup/full/db4/db4test.bson

STEP8:使用oplog,对该集合执行增量恢复操作
先查看对db4.db4test集合执行删除的开始时间

" : /db4.db4test/d 结果 /*1597326944,1)">db4.db4test" : ) } } ) } }

可以看到,删除的开始时间为:Timestamp(1597326944,1)。
执行增量恢复:

# 先处理oplog,删除文件oplog.$main.metadata.json,修改oplog.$main.bson为oplog.bson [root@mongo1 local]# # 执行恢复,root用户没权限导入,root2用户才有权限 mongorestore --authenticationDatabase admin -uroot2 -p123456 --port=27017 --oplogReplay --oplogLimit 1597326944:1STEP9:查看数据是否恢复,确认已经完全恢复回来

" }

到此,MongoDB 2.7主从复制环境基于时间点恢复已经测试完成。

 

 

 

补 充:用户root和root2权限信息                                                    

目前在导入数据时,使用具有root权限的超级用户进行数据导入,发现依然存在权限不走的提示。经过stackoverflow上面的提示,创建了root2用户来导入数据,不再报错。

stackoverflow:https://stackoverflow.com/questions/55208028/mongodb-applyops-not-authorized-on-admin-to-execute-command


root用户权限信息如下:具有userAdminAnyDatabase和root角色

> db.getUser(root)
{
     admin.rootuserdbadminroles : [
         {
             roleuserAdminAnyDatabase
         },{
             
         }
     ]
 }

root2用户权限信息如下,这里直接给出创建角色和用户的脚本

db.createRole( { role: interalUseOnlyOplogRestoretrue },actions: [ anyAction ] } ],roles: [] } ) db.createUser({ user: root2pwd: 123456 ] })

 

【完】

 

相关文档:

1.MongoDB 2.7主从复制(master –> slave)环境基于时间点的恢复  
2.MongoDB 4.2副本集环境基于时间点的恢复


3.MongoDB恢复探究:为什么oplogReplay参数只设置了日志应用结束时间oplogLimit,而没有设置开始时间?

相关文章

文章浏览阅读552次。com.mongodb.MongoQueryException: Quer...
文章浏览阅读635次,点赞9次,收藏8次。MongoDB 是一种 NoSQ...
文章浏览阅读2.1k次。和。_mongodb 日期类型
文章浏览阅读1.7k次。Scalestack等客户期待使用MongoDB Atla...
文章浏览阅读970次。SpringBoot整合中间件mongodb、ES_sprin...
文章浏览阅读673次。MongoDB 简介_尚医通sql