MongoDB 副本集的每一条修改操作都会记录一条 oplog日志,所以当数据库被误删后,可以通过重放现有的oplog来「尽可能的恢复数据」。
详细阅读完以上文章不难发现,oplog是有大小限制的[并且oplog文件是循环覆盖的,即当oplog写满时,最新的记录会覆盖最老的记录],可以根据自身的业务需求自定义oplog大小值。所以当业务IO读写量非常大,oplog大小设置较小时,如果误删数据,是有很大概率出现:由于oplog日志记录被覆盖而导致误删的数据无法恢复的场景。由此可见,合理配置oplog大小,可以在危机时刻最大程度的挽救业务数据的损失。
(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的覆盖范围:
(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
mongorestore -h <hostname><:port> -d db_name <path>
◆ --host <:port>, -h <:port>:MongoDB所在服务器地址,默认为: localhost:27017
◆ --db , -d :需要恢复的数据库实例
◆ --drop:恢复的时候,先删除当前数据,然后恢复备份的数据。就是说,恢复后,备份后添加修改的数据都会被删除,慎用哦!
◆ <path>:最后的一个参数,设置备份数据所在位置
◆ --dir:指定备份的目录
【注】不能同时指定 <path> 和 --dir 选项。
文章推荐:MongoDB自动备份脚本
备份形式会有多种:
◆ 通过mongodump等工具,对数据库进行逻辑备份(全量+增量)
◆ 通过拷贝dbpath目录产生的物理备份文件进行物理备份。
◆ 通过系统层面进行快照或者镜像进行文件系统级别备份。
【注】以上方式备份都是有实效性的,即固定时间段的数据备份,非实时。
(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
(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)
(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
问题分析:
如果备份开始的时间点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
挽救措施:
(1)首先停止业务,避免由于业务量大,导致把oplog 时间点B到时间点C之间的日子给覆盖了。
(2)立刻把oplog.rs集合给dump出来以便于进行时间点恢复。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。