有没有办法恢复MongoDB中最近删除的文件?

tre*_*rex 19 rollback mongodb

我错误地删除了上次查询中的一些文档,有没有办法回滚我的上一个查询mongo集合.

这是我的上一个查询:

 db.datas.remove({ "name" : "some_x_name"}) 
Run Code Online (Sandbox Code Playgroud)

有没有回滚/撤消选项?我可以收回我的数据吗?

Ada*_*ord 26

没有回滚选项(回滚在MongoDB上下文中具有不同的含义),严格来说没有支持的方法来获取这些文档 - 注释中涵盖了您可以/应该采取的预防措施.但是,如果您正在运行副本集,即使是单个节点副本集,那么您也有oplog.随着oplog覆盖在原稿插入,你可以将其恢复.

说明这一点的最简单方法是举个例子.我将使用一个简化的示例,只需要恢复100个已删除的文档.要超越这个(大量文档,或者您希望只选择性地恢复等),您将要么更改代码以迭代游标,或者使用您在MongoDB shell之外选择的语言来编写它.基本逻辑保持不变.

首先,让我们foo在数据库中创建我们的示例集合dropTest.我们将插入100个没有name字段的文档和100个具有相同name字段的文档,以便以后可以错误地删除它们:

use dropTest;
for(i=0; i < 100; i++){db.foo.insert({_id : i})};
for(i=100; i < 200; i++){db.foo.insert({_id : i, name : "some_x_name"})};
Run Code Online (Sandbox Code Playgroud)

现在,让我们模拟意外删除我们的100个name文档:

> db.foo.remove({ "name" : "some_x_name"})
WriteResult({ "nRemoved" : 100 })
Run Code Online (Sandbox Code Playgroud)

因为我们在副本集中运行,所以我们仍然在oplog(正在插入)中记录这些文档,并且幸运的是那些插入还没有(尚未)结束oplog(这oplog是一个有上限的集合记得).让我们看看我们是否能找到它们:

use local;
db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}).count();
100
Run Code Online (Sandbox Code Playgroud)

计数看起来正确,我们似乎还有我们的文件.我从经验中知道,oplog我们在这里需要的唯一条目就是o字段,所以让我们添加一个投影来仅返回(为了简洁而剪切输出,但你明白了):

db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}, {"o" : 1});
{ "o" : { "_id" : 100, "name" : "some_x_name" } }
{ "o" : { "_id" : 101, "name" : "some_x_name" } }
{ "o" : { "_id" : 102, "name" : "some_x_name" } }
{ "o" : { "_id" : 103, "name" : "some_x_name" } }
{ "o" : { "_id" : 104, "name" : "some_x_name" } }
Run Code Online (Sandbox Code Playgroud)

要重新插入这些文档,我们可以将它们存储在一个数组中,然后迭代数组并插入相关的部分.首先,让我们创建我们的数组:

var deletedDocs = db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}, {"o" : 1}).toArray();
> deletedDocs.length
100
Run Code Online (Sandbox Code Playgroud)

接下来我们提醒自己,我们现在只在集合中有100个文档,然后遍历100个插入,最后重新验证我们的计数:

use dropTest;
db.foo.count();
100
// simple for loop to re-insert the relevant elements
for (var i = 0; i < deletedDocs.length; i++) {
    db.foo.insert({_id : deletedDocs[i].o._id, name : deletedDocs[i].o.name});
}
// check total and name counts again
db.foo.count();
200
db.foo.count({name : "some_x_name"})
100
Run Code Online (Sandbox Code Playgroud)

你有它,有一些警告:

  • 这不是一个真正的恢复策略,查看备份(MMS,其他),延迟的辅助,如评论中所述
  • 在繁忙的大型系统上查询oplog中的文档(任何oplog查询是表扫描)并不会特别快.
  • 这些文档可能随时会在oplog中老化(当然,您可以复制oplog以供以后使用,以便为您提供更多时间)
  • 根据您的工作负载,您可能必须在重新插入结果之前对结果进行重复数据删除
  • 如图所示,较大的文档集对于数组而言太大,因此您需要迭代光标
  • 该格式oplog被视为内部格式,可能随时更改(恕不另行通知),因此使用风险由您自行承担


Yaz*_*zad 10

虽然我知道这有点旧,但我想分享一些我在这方面研究过的东西,这些东西可能对有类似问题的其他人有用.

事实是MongoDB没有立即物理删除数据 - 它只标记它被删除.然而,这是特定于版本的,并且目前没有文档或标准化 - 这可以使第三方工具开发人员(或迫切需要的人)能够构建工具或可靠地编写可跨版本工作的简单脚本.我为此开了一张票 - https://jira.mongodb.org/browse/DOCS-5151.

我确实探索了一个更低级别的选项,可能需要根据使用的MongoDB版本进行微调.可以理解的是,对于大多数人来说,链接的级别太低了,但是当其他所有方法都失败时它可以工作并且可以很方便.

我的方法涉及直接使用文件中的二进制文件,并使用Python脚本(或命令)来识别,读取和解包(BSON)已删除的数据.

我的方法受到这个 GitHub项目的启发(我不是这个项目的开发者).在我的博客上,我尝试简化脚本并从Raw MongoDB文件中提取特定的已删除记录.

目前,记录\xee在记录开头标记为删除为" ".这是原始db文件中删除的记录的样子,

‘\xee\xee\xee\xee\x07_id\x00U\x19\xa6g\x9f\xdf\x19\xc1\xads\xdb\xa8\x02name\x00\x04\x00\x00\x00AAA\x00\x01marks\x00\x00\x00\x00\x00\x00@\x9f@\x00?
Run Code Online (Sandbox Code Playgroud)

我用之前基于其他记录识别的记录大小替换了第一个块.

y=”3\x00\x00\x00?+x[20804:20800+51]
Run Code Online (Sandbox Code Playgroud)

最后使用BSON包(随附pymongo),我将二进制文件解码为Readable对象.

bson.decode_all(y)

[{u’_id': ObjectId(‘5519a6679fdf19c1ad73dba8?), u’name': u’AAA’, u’marks': 2000.0}]
Run Code Online (Sandbox Code Playgroud)

这个BSON现在是一个python对象,可以转储到recover集合中,或者只是记录在某个地方.

不用说,理想情况下,应该在数据库文件的备份副本上的暂存区域中完成此操作或任何其他恢复技术.

  • 我有一个非常大的数据转储来恢复,上面链接的第一个没有像@YazadKhambata那样正确处理Mongo 2.4上的已删除记录.所以我用Yazad的信息重写了这个脚本,并得到了这个:https://gist.github.com/guss77/f8e610cfddbe02c07896.我用它来从大型删除的集合中恢复数千条记录. (3认同)
  • 感谢您的回答!这似乎是罕见的信息.会非常有帮助!! (2认同)