MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Gibt es eine Möglichkeit, kürzlich gelöschte Dokumente in MongoDB wiederherzustellen?

Es gibt keine Rollback-Option (Rollback hat in einem MongoDB-Kontext eine andere Bedeutung), und genau genommen gibt es keine unterstützte Methode, um diese Dokumente zurückzubekommen – die Vorsichtsmaßnahmen, die Sie treffen können/sollten, werden in den Kommentaren behandelt. Wenn Sie jedoch einen Replikatsatz ausführen, sogar einen Replikatsatz mit einem einzigen Knoten, dann haben Sie ein oplog . Mit einem oplog das abdeckt, wann die Dokumente eingefügt wurden, können Sie sie möglicherweise wiederherstellen.

Am einfachsten lässt sich dies anhand eines Beispiels veranschaulichen. Ich werde ein vereinfachtes Beispiel mit nur 100 gelöschten Dokumenten verwenden, die wiederhergestellt werden müssen. Um darüber hinauszugehen (große Anzahl von Dokumenten oder vielleicht möchten Sie nur selektiv wiederherstellen usw.), möchten Sie entweder den Code ändern, um über einen Cursor zu iterieren, oder dies in der Sprache Ihrer Wahl außerhalb der MongoDB-Shell schreiben. Die Grundlogik bleibt gleich.

Lassen Sie uns zuerst unsere Beispielsammlung foo erstellen in der Datenbank dropTest . Wir fügen 100 Dokumente ohne name ein Feld und 100 Dokumente mit identischem name Feld, damit sie später versehentlich entfernt werden können:

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"})};

Lassen Sie uns nun das versehentliche Entfernen unseres 100 name simulieren Dokumente:

> db.foo.remove({ "name" : "some_x_name"})
WriteResult({ "nRemoved" : 100 })

Da wir in einem Replikatsatz arbeiten, haben wir immer noch eine Aufzeichnung dieser Dokumente im oplog (wird eingefügt) und zum Glück sind diese Einfügungen (noch) nicht vom Ende des oplog gefallen (das oplog ist eine begrenzte Sammlung, denken Sie daran). Mal sehen, ob wir sie finden können:

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

Die Zählung sieht korrekt aus, wir scheinen unsere Dokumente noch zu haben. Ich weiß aus Erfahrung, dass das einzige Stück des oplog Eintrag, den wir hier brauchen, ist das o Lassen Sie uns also eine Projektion hinzufügen, um nur das zurückzugeben (Ausgabe der Kürze halber gekürzt, aber Sie verstehen schon):

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" } }

Um diese Dokumente erneut einzufügen, können wir sie einfach in einem Array speichern, dann über das Array iterieren und die relevanten Teile einfügen. Lassen Sie uns zuerst unser Array erstellen:

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

Als Nächstes erinnern wir uns daran, dass wir jetzt nur 100 Dokumente in der Sammlung haben, durchlaufen dann die 100 Einfügungen und validieren schließlich unsere Zählungen erneut:

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

Und da haben Sie es, mit einigen Einschränkungen:

  • Dies soll keine echte Wiederherstellungsstrategie sein, schauen Sie sich Backups (MMS, andere) an, verzögerte Secondaries dafür, wie in den Kommentaren erwähnt
  • Es wird nicht besonders schnell gehen, die Dokumente auf einem großen ausgelasteten System aus dem Oplog abzufragen (jede Oplog-Abfrage ist ein Tabellenscan).
  • Die Dokumente können jederzeit aus dem Oplog veralten (Sie können natürlich eine Kopie des Oplogs zur späteren Verwendung erstellen, um mehr Zeit zu haben)
  • Abhängig von Ihrer Arbeitslast müssen Sie möglicherweise die Ergebnisse deduplizieren, bevor Sie sie erneut einfügen
  • Größere Dokumentsätze sind zu groß für ein Array, wie demonstriert, daher müssen Sie stattdessen über einen Cursor iterieren
  • Das Format des oplog gilt als intern und kann sich jederzeit (ohne Vorankündigung) ändern, also Nutzung auf eigene Gefahr