有没有办法恢复MongoDB中最近删除的文档?
Posted
技术标签:
【中文标题】有没有办法恢复MongoDB中最近删除的文档?【英文标题】:Is there any way to recover recently deleted documents in MongoDB? 【发布时间】:2014-11-06 07:23:36 【问题描述】:我在上次查询中错误地删除了一些文档,有什么办法可以回滚我上次查询的 mongo 集合。
这是我的最后一个查询:
db.foo.remove( "name" : "some_x_name")
是否有任何回滚/撤消选项?我可以取回我的数据吗?
【问题讨论】:
在这种情况下,如果您影响到有价值的数据,请务必定期备份;这适用于任何数据库技术 备份总是一个好主意,但您也可以延迟运行副本,让您可以选择纠正最近的错误,如下所示:docs.mongodb.org/manual/tutorial/… 如果您使用彩信,您可以进行时间点恢复。没有其他方法可以回滚!!!! 【参考方案1】:没有回滚选项(在 MongoDB 上下文中为rollback has a different meaning),严格来说,没有支持的方式来取回这些文档 - 您可以/应该采取的预防措施已包含在 cmets 中。然而,话虽如此,如果你正在运行一个副本集,甚至是一个单节点副本集,那么你就有一个oplog
。使用包含文档插入时间的oplog
,您可以恢复它们。
说明这一点的最简单方法是举个例子。我将使用一个简化的示例,其中只有 100 个需要恢复的已删除文档。要超越这一点(大量文档,或者您可能只希望有选择地恢复等),您将需要更改代码以迭代游标,或者在 MongoDB shell 之外使用您选择的语言编写它。基本逻辑保持不变。
首先,让我们在数据库dropTest
中创建我们的示例集合foo
。我们将插入 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");
现在,让我们模拟一下意外删除我们的 100 个name
文档:
> db.foo.remove( "name" : "some_x_name")
WriteResult( "nRemoved" : 100 )
因为我们在副本集中运行,所以我们仍然在 oplog
(正在插入)中有这些文档的记录,幸运的是,这些插入还没有(还)从 oplog
(@ 987654336@ 是capped collection 记住)。让我们看看能不能找到它们:
use local;
db.oplog.rs.find(op : "i", ns : "dropTest.foo", "o.name" : "some_x_name").count();
100
计数看起来正确,我们的文件似乎还在。我从经验中知道,我们在这里需要的唯一 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"
要重新插入这些文档,我们可以将它们存储在一个数组中,然后遍历该数组并插入相关的片段。首先,让我们创建我们的数组:
var deletedDocs = db.oplog.rs.find(op : "i", ns : "dropTest.foo", "o.name" : "some_x_name", "o" : 1).toArray();
> deletedDocs.length
100
接下来我们提醒自己,现在集合中只有 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
你有它,有一些警告:
这并不是真正的恢复策略,请查看备份(MMS,其他),为此延迟辅助,如 cmets 中所述 在大型繁忙系统上,从 oplog 中查询文档不会特别快(任何 oplog 查询都是表扫描)。 文档可能随时超出 oplog 的时间(当然,您可以制作 oplog 的副本以供以后使用以给您更多时间) 根据您的工作量,您可能需要在重新插入之前对结果进行重复数据删除 较大的文档集对于数组来说太大了,如所示,因此您需要迭代游标来代替oplog
的格式被视为内部格式,可能随时更改(恕不另行通知),因此使用风险自负
【讨论】:
“没有回滚选项”这仍然成立吗?我正在浏览文档,似乎当事务中止时,mongo 会中止所做的所有数据更改,因此半生不熟的更改永远不会变得可用docs.mongodb.com/manual/core/transactions 我敢肯定,自从我回答这个问题以来的 7 年里,这已经发生了巨大的变化,但恐怕我已经不再使用 MongoDB 并且已经有 6 年没有使用它了。如果您对当前功能有任何疑问,最好在 DBA Stackexchange 上提问【参考方案2】:虽然我知道这有点老了,但我想分享我在这个领域研究的一些东西,可能对其他有类似问题的人有用。
事实是 MongoDB 不会立即物理删除数据 - 它只是将其标记为删除。然而,这是特定于版本的,目前没有文档或标准化 - 这可以使第三方工具开发人员(或迫切需要的人)能够构建一个工具或编写一个可靠的跨版本工作的简单脚本。我为此开了一张票 - https://jira.mongodb.org/browse/DOCS-5151。
我确实探索了一个级别低得多的选项,可能需要根据使用的 MongoDB 版本进行微调。对于大多数人来说,链接级别太低是可以理解的,但是它可以工作并且在其他所有方法都失败时可以派上用场。
我的方法涉及直接使用文件中的二进制文件并使用 Python 脚本(或命令)来识别、读取和解包 (BSON) 已删除的数据。
我的方法受到this GitHub 项目的启发(我不是这个项目的开发者)。 Here on my blog 我尝试简化脚本并从原始 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′
我将第一个块替换为我之前根据其他记录确定的记录的大小。
y=”3\x00\x00\x00″+x[20804:20800+51]
最后使用 BSON 包(pymongo 附带),我将二进制文件解码为可读对象。
bson.decode_all(y)
[u’_id': ObjectId(‘5519a6679fdf19c1ad73dba8′), u’name': u’AAA’, u’marks': 2000.0]
这个 BSON 现在是一个 python 对象,可以转储到恢复集合中或简单地记录在某个地方。
不用说,这种恢复技术或任何其他恢复技术都应该在数据库文件备份副本上的暂存区域中完成。
【讨论】:
感谢您的回答!这似乎是罕见的信息。真的很有帮助!。 欢迎 - 我参与了我正在撰写的一篇论文,其中我还探讨了如何使用 Apache Cassandra 和 Apache HBase 在不同程度的成功中做类似的事情。 我有一个非常大的数据转储要恢复,上面链接的拳头不能像@YazadKhambata 那样正确处理 Mongo 2.4 上的已删除记录。所以我用 Yazad 的信息重写了要点中的脚本并得到了这个:gist.github.com/guss77/f8e610cfddbe02c07896。我用它从一个已删除的大型集合中恢复了数千条记录。 2019年有什么办法可以恢复collection remove()?以上是关于有没有办法恢复MongoDB中最近删除的文档?的主要内容,如果未能解决你的问题,请参考以下文章
mongodb数据被误删除,没有备份数据,只有日志和jonural文件,请问能恢复吗?