栏目头部广告

MongoDB全量备份+oplog增量备份数据恢复方案

一、MongoDB数据恢复背景简介

MongoDB 副本集的每一条修改操作都会记录一条 oplog日志,所以当数据库被误删后,可以通过重放现有的oplog来「尽可能的恢复数据」。

文章推荐:MongoDB Oplog详解

详细阅读完以上文章不难发现,oplog是有大小限制的[并且oplog文件是循环覆盖的,即当oplog写满时,最新的记录会覆盖最老的记录],可以根据自身的业务需求自定义oplog大小值。所以当业务IO读写量非常大,oplog大小设置较小时,如果误删数据,是有很大概率出现:由于oplog日志记录被覆盖而导致误删的数据无法恢复的场景。由此可见,合理配置oplog大小,可以在危机时刻最大程度的挽救业务数据的损失。

二、MongoDB数据备份mongodump与恢复mongorestore

2.1 mongodump工具用法

(1)常规用法

> mongodump -h db_host -d db_name -o db_directory -u user -p password

◆  -h 服务器IP地址
◆  -d 需要备份的数据库库名
◆  -o 备份的数据存放位置
◆  -u -p 如果有设置用户和密码,需要设置对应的用户名和密码,否则没有权限
◆  --oplog 实现热备,在备份时使用--oplog选项,会记录备份过程中的数据变化,会以oplog.bson保存下来。

【注】--oplog创建一个名为oplog.bson文件作为mongodump结果的一部分,包含了mongodump操作期间的oplog条目,实现了某个时间点的一致性快照。可用mongorestore--oplogReplay进行恢复。它的实际作用是在导出的同时生成一个oplog.bson文件,存放在你开始进行dump到dump结束之间所有的oplog。用图形来说明下oplog.bson的覆盖范围:

MongoDB全量备份+oplog增量备份数据恢复方案(图1)

(2)用于比较的正则表达式

◆  $gt 大于(>)
◆  $lt 小于(<)
◆  $gte 大于等于(>=)
◆  $lte 小于等于(<=)

【注】mongodump 的--query或-q选项可以指定查询时间范围。

(3)按时间查询数据—ISODate

[root@ansible ~]# db.person.find({"timestamp":{$gte:ISODate("2021-09-26T16:00:00Z")}}).count()

【注】ISODate指的是标准时间,东八时区相差8小时

(4)按时间查询数据—Date

# 将时间戳转换为毫秒,
[root@ansible ~]# date -d 2021-09-26 +%s # 2021年9月26日0时0分0秒
1632585600                               # 转换完成单位为s
1632585600000                            # 加三个0转换为ms

# 备份大约等于某个时间范围内的oplog
[root@ansible ~]# mongodump -h 192.168.0.193  --port 27017 -uroot -pUcloudcn --authenticationDatabase=admin -d local -c oplog.rs -q '{"timestamp":{$gte:Date(1632585600000)}}' -o /opt/oplogbackup

(5)按时间查询数据-Timestamp

# 备份某个时间范围内的oplog
[root@ansible ~]# mongodump -h 192.168.0.193  --port 27017 -uroot -pUcloudcn --authenticationDatabase=admin -d local -c oplog.rs -q '{ts:{$lt:Timestamp(1620273590, 1),$gt:Timestamp(1620272882, 11)}}' -o /opt/oplogbackup

2.2 mongorestore工具用法

mongorestore -h <hostname><:port> -d db_name <path>
◆  --host <:port>, -h <:port>:MongoDB所在服务器地址,默认为: localhost:27017
◆  --db , -d :需要恢复的数据库实例
◆  --drop:恢复的时候,先删除当前数据,然后恢复备份的数据。就是说,恢复后,备份后添加修改的数据都会被删除,慎用哦!
◆  <path>:最后的一个参数,设置备份数据所在位置
◆  --dir:指定备份的目录

【注】不能同时指定 <path> 和 --dir 选项。

文章推荐:MongoDB自动备份脚本

三、MongoDB数据丢失恢复方案

备份形式会有多种:

◆ 通过mongodump等工具,对数据库进行逻辑备份(全量+增量)
◆ 通过拷贝dbpath目录产生的物理备份文件进行物理备份。
◆ 通过系统层面进行快照或者镜像进行文件系统级别备份。

【注】以上方式备份都是有实效性的,即固定时间段的数据备份,非实时。

3.1 MongoDB进行库全量备份+oplog全量备份

(1)创建dbtest库和test集合,并插入100条文档

> use dbtest
switched to db dbtest
> db.createCollection("testuser")
{ "ok" : 1 }

> for(i=1;i<=100;i++){db.testuser.insert({"name": "star.gao"+i, age: i, created_time: new Date()})}
WriteResult({ "nInserted" : 1 })

> db.testuser.find()
{ "_id" : ObjectId("60b1ab58d2535c1eec549e6d"), "name" : "star.gao1", "age" : 1, "created_time" : ISODate("2021-05-29T02:47:52.744Z") }
{ "_id" : ObjectId("60b1ab58d2535c1eec549e6e"), "name" : "star.gao2", "age" : 2, "created_time" : ISODate("2021-05-29T02:47:52.749Z") }
{ "_id" : ObjectId("60b1ab58d2535c1eec549e6f"), "name" : "star.gao3", "age" : 3, "created_time" : ISODate("2021-05-29T02:47:52.751Z") }
{ "_id" : ObjectId("60b1ab58d2535c1eec549e70"), "name" : "star.gao4", "age" : 4, "created_time" : ISODate("2021-05-29T02:47:52.752Z") }
{ "_id" : ObjectId("60b1ab58d2535c1eec549e71"), "name" : "star.gao5", "age" : 5, "created_time" : ISODate("2021-05-29T02:47:52.754Z") }
{ "_id" : ObjectId("60b1ab58d2535c1eec549e72"), "name" : "star.gao6", "age" : 6, "created_time" : ISODate("2021-05-29T02:47:52.755Z") }
{ "_id" : ObjectId("60b1ab58d2535c1eec549e73"), "name" : "star.gao7", "age" : 7, "created_time" : ISODate("2021-05-29T02:47:52.757Z") }
{ "_id" : ObjectId("60b1ab58d2535c1eec549e74"), "name" : "star.gao8", "age" : 8, "created_time" : ISODate("2021-05-29T02:47:52.758Z") }
{ "_id" : ObjectId("60b1ab58d2535c1eec549e75"), "name" : "star.gao9", "age" : 9, "created_time" : ISODate("2021-05-29T02:47:52.760Z") }
……

> db.testuser.count() #插入了100条记录
100

(2)测试集合中有100条数据,测试做一个全量备份

[root@ansible ~]# mongodump --host=192.168.0.193 --port=27017 --authenticationDatabase=admin --oplog --out=/opt/fullbackup -uroot -pUcloudcn
2021-05-29T10:57:44.790+0800	writing admin.system.users to 
2021-05-29T10:57:44.794+0800	done dumping admin.system.users (6 documents)
2021-05-29T10:57:44.794+0800	writing admin.system.roles to 
2021-05-29T10:57:44.796+0800	done dumping admin.system.roles (1 document)
2021-05-29T10:57:44.797+0800	writing admin.system.version to 
2021-05-29T10:57:44.799+0800	done dumping admin.system.version (1 document)
2021-05-29T10:57:44.802+0800	writing dbtest.testuser to 
2021-05-29T10:57:44.802+0800	writing admin.tempusers to 
2021-05-29T10:57:44.806+0800	done dumping admin.tempusers (6 documents)
2021-05-29T10:57:44.806+0800	done dumping dbtest.testuser (100 documents)  #dbtest库testuser集合中的100条数据
2021-05-29T10:57:44.809+0800	writing captured oplog to 
2021-05-29T10:57:44.812+0800		dumped 1 oplog entry

MongoDB全量+增量+oplog恢复数据方案(图1)

MongoDB全量+增量+oplog恢复数据方案(图2)

(3)继续插入10条数据

> for(i=101;i<=110;i++){db.testuser.insert({"name": "star.gao"+i, age: i, created_time: new Date()})}
WriteResult({ "nInserted" : 1 })

> db.testuser.count()
110

【注】此时总计110条数据。

(4)模拟操作误删dbtest库下的testuser集合

> db.testuser.drop()
true

> show collections #查看当前库的集合列表

(5)备份现有的oplog.rs集合(表)

[root@ansible ~]# mongodump -h 192.168.0.193  --port 27017 -uroot -pUcloudcn --authenticationDatabase=admin -d local -c oplog.rs -o /opt/oplogbackup
2021-05-29T12:34:28.173+0800	writing local.oplog.rs to 
2021-05-29T12:34:28.179+0800	done dumping local.oplog.rs (337 documents)

(6)截取oplog获取误操作时间

方法一:从oplog备份中获取误操作时间

[root@ansible ~]# bsondump /opt/oplogbackup/local/oplog.rs.bson | grep "\"op\":\"c\""  |  grep "drop"
{"ts":{"$timestamp":{"t":1622257440,"i":1}},"h":{"$numberLong":"161066120901963990"},"v":2,"op":"c","ns":"dbtest.$cmd","o":{"drop":"testuser"}}

获取到oplog误删除时间点位置:"ts":{"$timestamp":{"t":1622257440,"i":1}}

方法二:登录到数据库,查询误操作时间

> use local
switched to db local

> db.oplog.rs.count()
337
> db.oplog.rs.find({"op": "c"}).sort({"ts": -1}).limit(1)
{ "ts" : Timestamp(1622257440, 1), "h" : NumberLong("161066120901963990"), "v" : 2, "op" : "c", "ns" : "dbtest.$cmd", "o" : { "drop" : "testuser" } }

获取到oplog误删除时间点位置:"ts" : Timestamp(1622257440, 1)

3.2 MongoDB数据恢复到drop误操作之前的数据

(1)查看oplog恢复窗口

> rs.printReplicationInfo();
configured oplog size:   2560MB
log length start to end: 337678secs (93.8hrs)   #恢复窗口约93h
oplog first event time:  Tue May 25 2021 13:16:02 GMT+0800 (CST)
oplog last event time:   Sat May 29 2021 11:04:00 GMT+0800 (CST)
now:                     Sat May 29 2021 13:24:07 GMT+0800 (CST)

【注】由于drop误操作1H前做的全备,oplog恢复窗口为93.8小时,所以可以把oplog备份出来的oplog.rs.bson文件覆盖全备时的oplog.bson文件。

(2)将oplog备份出来的oplog.rs.bson文件覆盖全备时的oplog.bson文件

[root@ansible ~]# cp /opt/oplogbackup/local/oplog.rs.bson /opt/fullbackup/oplog.bson 
cp: overwrite ‘/opt/fullbackup/oplog.bson’? y

(3)全量恢复数据

[root@ansible ~]# mongorestore -h 192.168.0.193 --port 27017 --oplogReplay --oplogLimit "1622257440:1" --drop  /opt/fullbackup/ -uroot -pUcloudcn 
2021-05-29T13:34:26.236+0800	preparing collections to restore from
2021-05-29T13:34:26.244+0800	reading metadata for dbtest.testuser from /opt/fullbackup/dbtest/testuser.metadata.json
2021-05-29T13:34:26.251+0800	reading metadata for admin.tempusers from /opt/fullbackup/admin/tempusers.metadata.json
2021-05-29T13:34:26.256+0800	restoring admin.tempusers from /opt/fullbackup/admin/tempusers.bson
2021-05-29T13:34:26.268+0800	restoring dbtest.testuser from /opt/fullbackup/dbtest/testuser.bson
2021-05-29T13:34:26.328+0800	no indexes to restore
2021-05-29T13:34:26.329+0800	finished restoring admin.tempusers (6 documents)
2021-05-29T13:34:26.332+0800	no indexes to restore
2021-05-29T13:34:26.333+0800	finished restoring dbtest.testuser (100 documents)
2021-05-29T13:34:26.333+0800	restoring users from /opt/fullbackup/admin/system.users.bson
2021-05-29T13:34:26.397+0800	restoring roles from /opt/fullbackup/admin/system.roles.bson
2021-05-29T13:34:26.423+0800	replaying oplog
2021-05-29T13:34:27.071+0800	done

> show dbs
admin   0.000GB
dbtest  0.000GB
local   0.000GB

> use dbtest
switched to db dbtest

> show tables #testuser集合恢复成功
testuser

> db.testuser.count() #集合110条文档恢复成功
110

四、关于使用oplog恢复数据场景分析

4.1 场景一:假设备份开始时间为 A 结束时间为B;等备份完成后,如果此时业务又insert或update了数据;在时间 C点,模拟误删除操作

问题分析:

如果备份开始的时间点A到误删除的时间点C 这段时间的oplog.rs的数据没有被覆盖那么就肯定能恢复出所有的数据。可以直接用oplog全量备份的oplog.rs.bson文件替换之前全量备份的产生的oplog.bson文件,然后恢复时使用--oplogReplay和--oplogLimit参数直接恢复。

恢复操作:

 (1) 恢复一致性全备

 [root@ansible ~]# mongorestore -h 192.168.0.193 --port 27017 --oplogReplay  /opt/fullbackup/ -uroot -pUcloudcn

(2)恢复最全的oplog

[root@ansible ~]# mongorestore -h 192.168.0.193 --port 27017 --oplogReplay --oplogLimit "1622257440:1" --drop  /opt/oplogbackup/local/oplog.rs.bson -uroot -pUcloudcn

4.2 场景二:如果备份开始的时间点A到你误删除的时间点C 这段时间的oplog.rs的数据被覆盖了,那么就只能先恢复你的全备,然后再从oplog.rs里面尽可能找到更多的关于这个集合的操作,然后应用,能不能全部恢复出数据,就看运气了。

挽救措施:

(1)首先停止业务,避免由于业务量大,导致把oplog 时间点B到时间点C之间的日子给覆盖了。

(2)立刻把oplog.rs集合给dump出来以便于进行时间点恢复。

作者:UStarGao
链接:https://www.starcto.com/mongodb/155.html
来源:STARCTO
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

UCloud云平台推荐


UCloud新用户专属注册连接

UCloud CDN超值特惠专场

UCloud全球云主机(UHost/VPS)大促页面

UCloud快杰云主机大促页面

文章页广告

随便看看

栏目底部广告
`