MongoDB误操作恢复测试

前序:

由于无论在什么架构下,都会不可避免的出现人为误操作的事故出现,本文就对可能出现的误操作问题的解决办法进行测试,这些都是本人想到的解决办法并加以测试实验

架构:Replica set(1Primary+1Secondary+1slaveDelay)

延时时间:600秒

Primary:192.168.1.100:27017

Secondary:192.168.1.100:27018

SlaveDelay:192.168.1.100:27019 #延时节点

步骤:

一、误删除collection的部分数据记录时:

我认为的两种情况:删除的数据存在于延时节点中、删除的数据未存在于延时节点中

删除的数据存在于延时节点中

这个时候直接从延时节点将误删除的数据导出来,再导入Primary节点即可

1、Primary库中目前数据情况:

trs1:PRIMARY>use tt

trs1:PRIMARY>db.t1.find()

{"_id" : ObjectId("583667ab6268d1913b424a9a"), "a": 1 }

{"_id" : ObjectId("583667ab6268d1913b424a9b"), "a": 2 }

{"_id" : ObjectId("583667ab6268d1913b424a9c"), "a": 3 }

{"_id" : ObjectId("583667ab6268d1913b424a9d"), "a": 4 }

{"_id" : ObjectId("583667ab6268d1913b424a9e"), "a": 5 }

{"_id" : ObjectId("583667ab6268d1913b424a9f"), "a": 6 }

{"_id" : ObjectId("583667ab6268d1913b424aa0"), "a": 7 }

{"_id" : ObjectId("583667ab6268d1913b424aa1"), "a": 8 }

{"_id" : ObjectId("583667ab6268d1913b424aa2"), "a": 9 }

{"_id" : ObjectId("583667ab6268d1913b424aa3"), "a": 10 }

延时节点中也是此数据

2、删除部分数据:

trs1:PRIMARY>db.t1.remove({a:{gte:4,lte:7}})

WriteResult({"nRemoved" : 4 })

trs1:PRIMARY>db.t1.find()

{"_id" : ObjectId("583667ab6268d1913b424a9a"), "a": 1 }

{"_id" : ObjectId("583667ab6268d1913b424a9b"), "a": 2 }

{"_id" : ObjectId("583667ab6268d1913b424a9c"), "a": 3 }

{"_id" : ObjectId("583667ab6268d1913b424aa1"), "a": 8 }

{"_id" : ObjectId("583667ab6268d1913b424aa2"), "a": 9 }

{"_id" : ObjectId("583667ab6268d1913b424aa3"), "a": 10 }

3、在延时节点中将被删除数据导出:

[mongo@localhost~]mongoexport --host 10.25.161.15:27019 -d tt -c t1 -q '{a:{gte:4,$lte:7}}'--out ~/backups/myRecords.json

2016-11-24T12:24:17.643+0800 connected to: 10.25.161.15:27019

2016-11-24T12:24:17.644+0800 exported 4 records

4、将数据导入到Primary库集合中:

[mongo@localhost~]$ mongoimport --host 10.25.161.15:27017 -d tt -c t1 --file ~/backups/myRecords.json

2016-11-24T12:25:09.962+0800 connected to: 10.25.161.15:27017

2016-11-24T12:25:09.972+0800 imported 4 documents

5、查询数据恢复结果:

trs1:PRIMARY>db.t1.find().sort({a:1})

{"_id" : ObjectId("583667ab6268d1913b424a9a"), "a": 1 }

{"_id" : ObjectId("583667ab6268d1913b424a9b"), "a": 2 }

{"_id" : ObjectId("583667ab6268d1913b424a9c"), "a": 3 }

{"_id" : ObjectId("583667ab6268d1913b424a9d"), "a": 4 }

{"_id" : ObjectId("583667ab6268d1913b424a9e"), "a": 5 }

{"_id" : ObjectId("583667ab6268d1913b424a9f"), "a": 6 }

{"_id" : ObjectId("583667ab6268d1913b424aa0"), "a": 7 }

{"_id" : ObjectId("583667ab6268d1913b424aa1"), "a": 8 }

{"_id" : ObjectId("583667ab6268d1913b424aa2"), "a": 9 }

{"_id" : ObjectId("583667ab6268d1913b424aa3"), "a": 10 }

查看已经恢复回来了,但是这算是在理想状态下:未有业务继续写入,导入导出数据量小,所以不知道在生产环境下还能否顺利实现,还需要待后续实验

删除的数据未存在于延时节点中

这个时候就需要使用oplog日志恢复了,将误删除的数据用oplog恢复回来

1、查看Primary库数据信息

trs1:PRIMARY>db.t1.find().sort({a:1})

{"_id" : ObjectId("583667ab6268d1913b424a9a"), "a": 1 }

{"_id" : ObjectId("583667ab6268d1913b424a9b"), "a": 2 }

{"_id" : ObjectId("583667ab6268d1913b424a9c"), "a": 3 }

{"_id" : ObjectId("583667ab6268d1913b424a9d"), "a": 4 }

{"_id" : ObjectId("583667ab6268d1913b424a9e"), "a": 5 }

{"_id" : ObjectId("583667ab6268d1913b424a9f"), "a": 6 }

{"_id" : ObjectId("583667ab6268d1913b424aa0"), "a": 7 }

{"_id" : ObjectId("583667ab6268d1913b424aa1"), "a": 8 }

{"_id" : ObjectId("583667ab6268d1913b424aa2"), "a": 9 }

{"_id" : ObjectId("583667ab6268d1913b424aa3"), "a": 10 }

2、查看时间戳s1

trs1:PRIMARY>rs.status().members[0].optime.ts

Timestamp(1479961509,4)

3、插入10条数据

trs1:PRIMARY>for(var i=1;i<11;i++){

...db.t1.insert({b:i})

...}

WriteResult({"nInserted" : 1 })

3、查看时间戳s2

trs1:PRIMARY>rs.status().members[0].optime.ts

Timestamp(1479972707,10)

4、删除5条数据(模拟误删除)

trs1:PRIMARY>db.t1.remove({b:{$gte:6}})

WriteResult({"nRemoved" : 5 })

5、再插入1条数据(模拟误操作之后又有业务进行了insert动作)

trs1:PRIMARY>db.t1.insert({c:1})

WriteResult({"nInserted" : 1 })

6、mongobackup备份导出时间戳s1之后的oplog

mongobackup-h 10.25.161.15 -port 27017 --backup -s 1479961509,4

7、使用mongobackup进行oplog恢复s1与s2时间段内的数据

mongobackup-h 10.25.161.15 -port 27017 --recovery -s 1479961509,4 -t 1479972707,10

connectedto: 10.25.161.15:27017

ThuNov 24 15:36:34.005 Replaying file oplog.bson

ThuNov 24 15:36:34.006 Only applying oplog entries matching this criteria: {"ts" : { "gte" : { "timestamp" : {"t" : 1479961509, "i" : 4 } }, "lte" : {"timestamp" : { "t" : 1479972707, "i" : 10 } } }}

16objects found

ThuNov 24 15:36:34.006 Successfully Recovered.

8、查询恢复后的数据

trs1:PRIMARY>db.t1.find({b:10})

{"_id" : ObjectId("58369763c95ee8a72cfab654"), "b": 10 }

trs1:PRIMARY>db.t1.find({b:{gte:1,lte:10}})

{"_id" : ObjectId("58369763c95ee8a72cfab64b"), "b": 1 }

{"_id" : ObjectId("58369763c95ee8a72cfab64c"), "b": 2 }

{"_id" : ObjectId("58369763c95ee8a72cfab64d"), "b": 3 }

{"_id" : ObjectId("58369763c95ee8a72cfab64e"), "b": 4 }

{"_id" : ObjectId("58369763c95ee8a72cfab64f"), "b": 5 }

{"_id" : ObjectId("58369763c95ee8a72cfab650"), "b": 6 }

{"_id" : ObjectId("58369763c95ee8a72cfab651"), "b": 7 }

{"_id" : ObjectId("58369763c95ee8a72cfab652"), "b": 8 }

{"_id" : ObjectId("58369763c95ee8a72cfab653"), "b": 9 }

{"_id" : ObjectId("58369763c95ee8a72cfab654"), "b": 10 }

trs1:PRIMARY>db.t1.find({c:1})

{"_id" : ObjectId("583697d1c95ee8a72cfab655"), "c": 1 }

查询之后我们发现原来删除的{b:6}~{b:10}的数据都恢复了,并且{c:1}的这笔数据也没有被清除,达到了我们预计的结果,其实实际中整个操作的难点在于时间戳的确认,我先在理想情况下把实验做完,后续再进行补充说明实际情况下的时间戳确认。

二、误删除collection

有时候误删除整个collection的话,就没办法仅通过延迟节点恢复了,因为延迟节点不存在最近一次同步之后Primary更新的数据,这就需要oplog闪亮登场了

在我目前的认知看来,可以有两种恢复方法:延时节点collection+oplog恢复、mongodump备份+oplog恢复

延时节点collection+oplog恢复:

1、查看表数据量

trs1:PRIMARY>db.t1.find()

{"_id" : ObjectId("5836a4c7c95ee8a72cfab656"), "a": 1 }

{"_id" : ObjectId("5836a4c7c95ee8a72cfab657"), "a": 2 }

{"_id" : ObjectId("5836a4c7c95ee8a72cfab658"), "a": 3 }

{"_id" : ObjectId("5836a4c7c95ee8a72cfab659"), "a": 4 }

{"_id" : ObjectId("5836a4c7c95ee8a72cfab65a"), "a": 5 }

2、插入一条新记录(模拟延迟节点没有的数据)

trs1:PRIMARY>db.t1.insert({a:6})

WriteResult({"nInserted" : 1 })

3、Drop collection

trs1:PRIMARY>db.t1.drop()

true

4、查看延时节点最近一次同步的时间戳s1

trs1:SECONDARY>rs.status().members[2].optime.ts

Timestamp(1479976135, 6)

5、mongoexport导出延时节点中collection的所有数据

[mongo@localhostbackups]$ mongoexport --host 10.25.161.15:27019 -d tt -c t1

2016-11-24T17:31:24.970+0800 connected to: 10.25.161.15:27019

{"_id":{"$oid":"5836a4c7c95ee8a72cfab656"},"a":1.0}

{"_id":{"$oid":"5836a4c7c95ee8a72cfab657"},"a":2.0}

{"_id":{"$oid":"5836a4c7c95ee8a72cfab658"},"a":3.0}

{"_id":{"$oid":"5836a4c7c95ee8a72cfab659"},"a":4.0}

{"_id":{"$oid":"5836a4c7c95ee8a72cfab65a"},"a":5.0}

2016-11-24T17:31:24.971+0800 exported 5 records

6、mongobackup导出时间戳s1之后的oplog

[mongo@localhostbackups]$ mongobackup -h 10.25.161.15 -port 27017 --backup -s 1479976135,6

connectedto: 10.25.161.15:27017

ThuNov 24 17:32:14.457 local.oplog.rs to backup/oplog.bson

ThuNov 24 17:32:14.458 2objects

7、mongoimport第④步中的json数据

[mongo@localhostbackups]$ mongoimport --host 10.25.161.15:27017 -d tt -c t1 --file ./myRecords.json

2016-11-24T17:34:55.200+0800 connected to: 10.25.161.15:27017

2016-11-24T17:34:55.229+0800 imported 5 documents

8、查询drop前的最后一个时间戳s2

因为如果选择drop的时间戳的话,mongobackup恢复时仍然是会重放drop动作的(我已实验过,注意第⑨步中的红色字体就明白了)

[mongo@localhostbackups]$ bsondump backup/oplog.bson | grep -B 1'"drop":"t1"'

。。。。。。。

{"ts":{"timestamp":{"t":1479979785,"i":1}},"t":{"numberLong":"4"},"h":{"numberLong":"-1488414770385429463"},"v":2,"op":"i","ns":"tt.t1","o":{"_id":{"oid":"5836b309c95ee8a72cfab65b"},"a":6.0}}

{"ts":{"timestamp":{"t":1479979814,"i":1}},"t":{"numberLong":"4"},"h":{"numberLong":"-1645421116110840065"},"v":2,"op":"c","ns":"tt.cmd","o":{"drop":"t1"}}

9、mongobackup恢复collection到drop之前状态

mongobackup-h 10.25.161.15 -port 27017 --recovery -s 1479976135,6 -t 1479979785,1

connectedto: 10.25.161.15:27017

ThuNov 24 17:39:49.289 Replaying file oplog.bson

ThuNov 24 17:39:49.289 Only applying oplog entries matching this criteria: {"ts" : { "gte" : {"timestamp" : { "t" : 1479976135, "i" : 6 } },"lte" : { "timestamp" : {"t" : 1479979785, "i" : 1 } } } }

2objects found

ThuNov 24 17:39:49.290 Successfully Recovered.

10、验证结果

trs1:PRIMARY>db.t1.find()

{"_id" : ObjectId("5836a4c7c95ee8a72cfab658"), "a": 3 }

{"_id" : ObjectId("5836a4c7c95ee8a72cfab656"), "a": 1 }

{"_id" : ObjectId("5836a4c7c95ee8a72cfab657"), "a": 2 }

{"_id" : ObjectId("5836a4c7c95ee8a72cfab65a"), "a": 5 }

{"_id" : ObjectId("5836a4c7c95ee8a72cfab659"), "a": 4 }

{"_id" : ObjectId("5836b309c95ee8a72cfab65b"), "a": 6 }

mongodump备份+oplog恢复:

开启实时oplog备份
[mongo@localhostbackups]$ pwd

/home/mongo/backups

[mongo@localhostbackups]mongobackup -h 10.25.161.15 --port 27017 --backup --stream

插入10万笔数据(模拟dump备份时有新数据生成)
trs1:PRIMARY>for(var i=0;i<100000;i++){

...db.t1.insert({a:i})}

WriteResult({"nInserted" : 1 })

mongodump执行全量备份
[mongo@localhostbackups]$ pwd

/home/mongo/backups

[mongo@localhostbackups]$mongodump --host 10.25.161.15:27017 --oplog

2016-11-25T11:24:56.731+0800 writing tt.t1 to

2016-11-25T11:24:56.762+0800 done dumping tt.t1 (4239 documents)

2016-11-25T11:24:56.763+0800 writing captured oplog to

2016-11-25T11:24:56.769+0800 dumped 36 oplog entries

待insert完成后,drop掉此collection(模拟误删除collection动作)
trs1:PRIMARY>db.t1.count()

100000

trs1:PRIMARY>db.t1.drop()

true

停止mongobackup的实时oplog备份
使用Ctrl+C就可以停止

查看第③步备份中最后一笔oplog的时间戳s1
[mongo@localhostbackups]$ pwd

/home/mongo/backups

[mongo@localhostbackups]$ ll dump/

total4

-rw-rw-r--.1 mongo mongo 3816 Nov 25 11:24oplog.bson

drwxrwxr-x.2 mongo mongo 43 Nov 25 11:24 tt

[mongo@localhostbackups]$ bsondump dump/oplog.bson | tail -1

2016-11-25T11:35:26.735+0800 36 objects found

{"ts":{"timestamp":{"t":1480044296,"i":776}},"t":{"numberLong":"4"},"h":{"numberLong":"4007452313334291247"},"v":2,"op":"i","ns":"tt.t1","o":{"_id":{"oid":"5837af0823358cbda370c7b0"},"a":4267.0}}

注:因为mongodump在备份时,数据一直在变化,所以为了备份数据一致性使用了--oplog参数,用于备份在mongodump过程中新生成的数据,所以oplog.bson最后一笔数据的时间戳就是mongodump结束时的时间戳

首先使用全量备份进行恢复
[mongo@localhostbackups]$ mongorestore --host10.25.161.15:27017 --oplogReplay ./dump

2016-11-25T16:15:15.149+0800 building a list of dbs and collections torestore from dump dir

2016-11-25T16:15:15.151+0800 reading metadata for tt.t1 fromdump/tt/t1.metadata.json

2016-11-25T16:15:15.167+0800 restoring tt.t1 from dump/tt/t1.bson

2016-11-25T16:15:15.368+0800 restoring indexes for collection tt.t1 frommetadata

2016-11-25T16:15:15.369+0800 finished restoring tt.t1 (4239 documents)

2016-11-25T16:15:15.369+0800 replaying oplog

2016-11-25T16:15:15.387+0800 done

trs1:PRIMARY>db.t1.count()

4268

通过bsondump查看drop collection之前的最后一个时间戳s2
$bsondump backup/oplog000000.bson | grep -B 1'"drop":"t1"' | sort

{"ts":{"timestamp":{"t":1480044395,"i":807}},"t":{"numberLong":"4"},"h":{"numberLong":"-7281969221530393253"},"v":2,"op":"i","ns":"tt.t1","o":{"_id":{"oid":"5837af6b23358cbda3723da4"},"a":99999.0}}

{"ts":{"timestamp":{"t":1480044421,"i":1}},"t":{"numberLong":"4"},"h":{"numberLong":"8681873880357004177"},"v":2,"op":"c","ns":"tt.cmd","o":{"drop":"t1"}}

注:如果有多行drop记录,则使用sort选取最后一个drop动作的前一个时间戳(即上述红色数字)

mongobackup恢复时间戳s1~s2之间的数据
[mongo@localhostbackups]$ mongobackup -h 10.25.161.15 --port 27017 --recovery -s 1480044296,776 -t 1480044395,807 ./backup/

connectedto: 10.25.161.15:27017

FriNov 25 16:17:35.343 Replaying file oplog.bson

FriNov 25 16:17:35.343 Only applying oplog entries matching this criteria: {"ts" : { "gte" : { "timestamp" : {"t" : 1480044296, "i" : 776 } }, "lte" : {"timestamp" : { "t" : 1480044395, "i" : 807 } }} }

100103objects found

FriNov 25 16:17:35.962 Successfully Recovered.

验证恢复情况
trs1:PRIMARY>db.t1.count()

100000
————————————————
版权声明:本文为CSDN博主「逃跑的肉丸」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jianlong727/article/details/53485507

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容

  • 在生产环境中,尽管我们尽力避免误操作,但是还是会遇到误操作或是其他情况的出现。这时候我们就需要进行Point in...
    张伟科阅读 3,717评论 2 3
  • 一、MongoDB简介 1.概述 ​ MongoDB是一个基于分布式文件存储的数据库,由C++语言编写。旨在为WE...
    郑元吉阅读 978评论 0 2
  • 第一章:逻辑结构 第二章:安装部署 1、系统准备 2、mongodb安装 创建所需用户和组 创建mongodb所需...
    zwb_jianshu阅读 1,600评论 0 1
  • 第一章:逻辑结构 第二章:安装部署 1、系统准备 2、mongodb安装 创建所需用户和组 创建mongodb所需...
    极光01阅读 400评论 0 0
  • 简介 MongoDB 是一个基于分布式文件存储的NoSQL数据库 由C++语言编写,运行稳定,性能高 旨在为 WE...
    大熊_7d48阅读 37,127评论 1 9