MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

C'è un modo per recuperare i documenti eliminati di recente in MongoDB?

Non esiste un'opzione di rollback (il rollback ha un significato diverso in un contesto MongoDB) e, a rigor di termini, non esiste un modo supportato per recuperare questi documenti:le precauzioni che puoi/dovresti prendere sono trattate nei commenti. Detto questo, tuttavia, se stai eseguendo un set di repliche, anche un set di repliche a nodo singolo, hai un oplog . Con un oplog che copre quando i documenti sono stati inseriti, potresti essere in grado di recuperarli.

Il modo più semplice per illustrare questo è con un esempio. Userò un esempio semplificato con solo 100 documenti eliminati che devono essere ripristinati. Per andare oltre (un numero enorme di documenti, o forse desideri ripristinare solo in modo selettivo ecc.) vorrai cambiare il codice per scorrere su un cursore o scriverlo usando la tua lingua preferita al di fuori della shell MongoDB. La logica di base rimane la stessa.

Per prima cosa, creiamo la nostra raccolta di esempio foo nel database dropTest . Inseriamo 100 documenti senza un name campo e 100 documenti con un identico name campo in modo che possano essere erroneamente rimossi in seguito:

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

Ora simuliamo la rimozione accidentale dei nostri 100 name documenti:

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

Poiché stiamo eseguendo in un set di repliche, abbiamo ancora un record di questi documenti nel oplog (in fase di inserimento) e per fortuna quegli inserti non sono (ancora) caduti alla fine del oplog (il oplog è una collezione limitata ricorda) . Vediamo se riusciamo a trovarli:

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

Il conteggio sembra corretto, sembra che abbiamo ancora i nostri documenti. So per esperienza che l'unico pezzo dell'oplog la voce di cui avremo bisogno qui è il o campo, quindi aggiungiamo una proiezione per restituire solo quello (output tagliato per brevità, ma hai un'idea):

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

Per reinserire quei documenti, possiamo semplicemente archiviarli in un array, quindi scorrere l'array e inserire i pezzi rilevanti. Per prima cosa, creiamo il nostro array:

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

Quindi ricordiamo a noi stessi che ora abbiamo solo 100 documenti nella raccolta, quindi esegui il ciclo sui 100 inserti e infine riconvalidiamo i nostri conteggi:

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

E il gioco è fatto, con alcuni avvertimenti:

  • Questa non vuole essere una vera strategia di ripristino, guarda i backup (MMS, altro), secondari ritardati per quello, come menzionato nei commenti
  • Non sarà particolarmente veloce interrogare i documenti fuori dall'oplog (qualsiasi query oplog è una scansione di tabella) su un sistema molto occupato.
  • I documenti potrebbero uscire dall'oplog in qualsiasi momento (puoi, ovviamente, fare una copia dell'oplog per un uso successivo per avere più tempo)
  • A seconda del carico di lavoro, potresti dover deduplicare i risultati prima di reinserirli
  • Insiemi di documenti più grandi saranno troppo grandi per un array come mostrato, quindi dovrai invece scorrere su un cursore
  • Il formato del oplog è considerato interno e può cambiare in qualsiasi momento (senza preavviso), quindi utilizzalo a tuo rischio