将mongodb数据按需回档到任意时间点

在生产环境中,尽管我们尽力避免误操作,但是还是会遇到误操作或是其他情况的出现。这时候我们就需要进行Point in time recovery了。Mongodb的point in time recovery是基于oplog进行的,所以请确保oplog的size足够大,也请确保定时有冷备份(或是延时备份)。

理论上只要我们的mongodump做得足够频繁再结合oplog,是可以保证数据库能够恢复到过去的任意一个时间点的。MMS(现在叫Cloud Manager)的付费备份也正是在利用这个原理工作。

切记:在出现问题的时候,我们第一时间要做的时保护犯罪现场,停止应用写入,保护数据库数据与状态。有可能的话,在进行恢复之前,将现在的oplog进行一次全备份,也将现在数据进行一次全备份。

根据point in time恢复的前提条件:

1)必须在oplog时间窗口期内有完整的数据备份;

2)最好在误操作之后就立马停止数据库的写入操作;

下面是通过冷备份+oplog进行point in time recovery的例子(例子仅供参考,实际中请根据情况来操作,过程需严谨)

第一步:首先创造点数据

use test

for(var i = 0; i < 1000; i++) {

  db.foo.insert({a: i});

}

第二步:更改点数据

rep:PRIMARY>db.foo.update({"a":999},{$set:{"a":1000}})

第三步:做一次完全备份

$ mongodump -h 127.0.0.1 --port 27017 -d test -o /backup

第四步:插入一些数据

rep:PRIMARY>db.foo.insert({"a":2000})

第五步:删除一条数据(模拟误操作)

rep:PRIMARY>db.foo.remove({"a":10})

第六步:再次插入一条数据

rep:PRIMARY>db.foo.insert({"a":3000})

查看现在一共有多少条数据

rep:PRIMARY> db.foo.count()

1001

我们现在想要取消第五步操作(remove操作,假设其为误操作),但是不影响其他数据。

我们先整体备份oplog(保护犯罪现场,哈哈,防止出现其他问题,有可能的话,还可以将现在的数据进行一次备份,防止如果操作失败造成的数据问题。)

# mongodump --port 27017 -d local -c oplog.rs -o /tmp/oplog/

第一步:找到误操作语句的时间戳

首先需要查找出操作失误的步骤,我们根据条件来匹配查找,条件越多对于定位误操作语句越有效。一般操作的动作肯定知道,其二是操作的库和集合肯定知道,最后如果知道大概的时间点就更好了,但这些并不能保证100%能够找到误操作的语句。

rep:PRIMARY> db.oplog.rs.find({"op" : "d", "ns" : "test.foo"}).pretty()

{

  "ts" : Timestamp(1481095782, 2),

  "t" : NumberLong(1),

  "h" : NumberLong("-8377102158449591320"),

  "v" : 2,

  "op" : "d",

  "ns" : "test.foo",

  "o" : {

  "_id" : ObjectId("58466e862780d8f6f6594dd0")

  }

}

可以拿着这个ObjectId去备份数据中验证一下。

现在已经找到了误操作的语句后,接下来就是要找出完整备份之后和删除(误操作)之前的所有操作。

第二步:找到备份的最后一条语句的操作记录的时间戳

$ bsondump /backup/test/foo.bson | tail -n1

{"_id":{"$oid":"58466e872780d8f6f65951ad"},"a":1000.0}

rep:PRIMARY>db.oplog.rs.find({"ns" : "test.foo","o":{"_id" : ObjectId("58466e872780d8f6f65951ad"),"a":1000}})

{ "ts" : Timestamp(1481094522, 1), "t" : NumberLong(1), "h" : NumberLong("-7286476219427328778"), "v" : 2, "op" : "u", "ns" :

"test.foo", "o2" : { "_id" : ObjectId("58466e872780d8f6f65951ad") }, "o" : { "_id" : ObjectId("58466e872780d8f6f65951ad"), "a" : 1000 } }

PS:我个人感觉使用文档去查还是精确的,操作一般无非就是insert或update,根据其文档就可以查出这条语句的最后一步操作。

第三步:根据误操作语句的时间戳和备份数据最后一条操作语句的时间戳找到这中间的所有操作语句

rep:PRIMARY> db.oplog.rs.find({ts:{$lt:Timestamp(1481095782, 2),$gt:Timestamp(1481094522, 1)}}).pretty()

{

  "ts" : Timestamp(1481095782, 1),

  "t" : NumberLong(1),

  "h" : NumberLong("-6509530327776617288"),

  "v" : 2,

  "op" : "i",

  "ns" : "test.foo",

  "o" : {

  "_id" : ObjectId("5847ba662780d8f6f65951b2"),

  "a" : 2000

  }

}

由于生产环境中会是很多操作,所以将其dump出来。

1$ mongodump --port 27017 -d local -c oplog.rs -q '{ts:{$lt:Timestamp(1481095782, 2),$gt:Timestamp(1481094522, 1)}}' -o /tmp/restore-1

第四步:再根据误操作时的时间戳找出之后的操作语句

rep:PRIMARY> db.oplog.rs.find({ts:{$gt:Timestamp(1481095782, 2)}}).pretty()

{

  "ts" : Timestamp(1481095783, 1),

  "t" : NumberLong(1),

  "h" : NumberLong("-7552219668757596741"),

  "v" : 2,

  "op" : "i",

  "ns" : "test.foo",

  "o" : {

  "_id" : ObjectId("5847ba672780d8f6f65951b3"),

  "a" : 3000

  }

}

同样将其dump出来

$ mongodump --port 27017 -d local -c oplog.rs -q '{ts:{$gt:Timestamp(1481095782, 2)}}' -o /backup/restore-2

第五步:进行数据恢复

现在有了一次完整备份加上oplog提取的操作就可以进行数据的恢复了。

rep:PRIMARY> db.foo.drop()

true

做前期准备,把restore-1中的oplog.rs.bson复制到完整备份目录下,名称为oplog.bson。

$ cp -fr /tmp/restore-1/local/oplog.rs.bson /backup/oplog.bson

$ ll /backup

drwxr-xr-x. 2 root root 4096 Dec 7 19:43 test

-rwxr-xr-x. 1 root root 210  Dec 7 19:43 oplog.bson

然后使用mongorestore进行完整备份数据的恢复,并且加上–oplogReplay参数会同时把resetore-1提取的操作进行重放(恢复第四步操作)。

$ mongorestore --port 27017 --oplogReplay  /backup/

2016-12-07T19:18:59.389+0800  building a list of dbs and collections to restore from /backup dir

2016-12-07T19:18:59.392+0800  reading metadata for test.foo from /backup/test/foo.metadata.json

2016-12-07T19:18:59.392+0800  reading metadata for test.oplog.rs from /backup/test/oplog.rs.metadata.json

2016-12-07T19:18:59.392+0800  restoring test.oplog.rs from /backup/test/oplog.rs.bson

2016-12-07T19:18:59.446+0800  restoring test.foo from /backup/test/foo.bson

2016-12-07T19:18:59.495+0800  no indexes to restore

2016-12-07T19:18:59.496+0800  finished restoring test.oplog.rs (6 documents)

2016-12-07T19:18:59.688+0800  restoring indexes for collection test.foo from metadata

2016-12-07T19:18:59.689+0800  finished restoring test.foo (1000 documents)

2016-12-07T19:18:59.689+0800  replaying oplog

2016-12-07T19:18:59.693+0800  done

进入到mongodb查看数据恢复情况。

rep:PRIMARY> db.foo.count()

1001

rep:PRIMARY> db.foo.find({"a":2000})

{ "_id" : ObjectId("5847ba662780d8f6f65951b2"), "a" : 2000 }

rep:PRIMARY> db.foo.find({"a":10})

{ "_id" : ObjectId("58466e862780d8f6f6594dd0"), "a" : 10 }

rep:PRIMARY> db.foo.find({"a":3000})

rep:PRIMARY>

可以看出误操作之前的数据都已经恢复,只有误操作之后的数据还没有。所以接下来就可以接着来恢复误操作之后的数据(恢复第六步操作)。

再重新备份一次,然后同样把restore-2中的oplog.rs.bson复制到备份目录。

$ mv /backup/* /tmp/

$ mongodump -h 127.0.0.1 --port 27017 -d test -o /backup

2016-12-07T19:36:18.917+0800 writing test.foo to

2016-12-07T19:36:18.917+0800 writing test.oplog to

2016-12-07T19:36:18.918+0800 done dumping test.oplog (2 documents)

2016-12-07T19:36:18.922+0800 done dumping test.foo (1001 documents)

1$ cp -fr /tmp/restore-2/local/oplog.rs.bson /backup/oplog.bson

然后再次使用mongorestore进行恢复。

rep:PRIMARY> db.foo.drop()

true

$ mongorestore --port 27017 --oplogReplay  /backup/

最后再次进入到mongo中查看误操作之后的数据是否存在。

rep:PRIMARY> db.foo.count()

1002

rep:PRIMARY> db.foo.find({"a":3000})

{ "_id" : ObjectId("5847ba672780d8f6f65951b3"), "a" : 3000 }

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

推荐阅读更多精彩内容

  • NoSql数据库优缺点 在优势方面主要体现在下面几点: 简单的扩展 快速的读写 低廉的成本 灵活的数据模型 在不足...
    dreamer_lk阅读 2,681评论 0 6
  • 本次学习使用的是麦子学院的《mongodb最佳实践课程》.侵删. 安装数据库 在ubuntu下mongodb默认已...
    whaike阅读 1,359评论 1 6
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,494评论 18 139
  • Built-In Roles(内置角色): 1. 数据库用户角色:read、readWrite; 2. 数据库管理...
    阿杰Alex阅读 1,382评论 0 1
  • 我是那种一上车就准备睡觉度过的人,但现在一点也睡不着。 小时候晕车的很严重,长大后却因为种种无可奈何需要坐车,便开...
    条形码0908阅读 268评论 0 0