MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Existe alguma maneira de recuperar documentos excluídos recentemente no MongoDB?


Não há opção de reversão (reversão tem um significado diferente no contexto do MongoDB) e, estritamente falando, não há uma maneira suportada de recuperar esses documentos - as precauções que você pode/deve tomar são abordadas nos comentários. Com isso dito, no entanto, se você estiver executando um conjunto de réplicas, mesmo um conjunto de réplicas de nó único, você terá um oplog . Com um oplog que cobre quando os documentos foram inseridos, você poderá recuperá-los.

A maneira mais fácil de ilustrar isso é com um exemplo. Usarei um exemplo simplificado com apenas 100 documentos excluídos que precisam ser restaurados. Para ir além disso (enorme número de documentos, ou talvez você queira apenas restaurar seletivamente etc.), você vai querer alterar o código para iterar sobre um cursor ou escrever isso usando sua linguagem de escolha fora do shell do MongoDB. A lógica básica continua a mesma.

Primeiro, vamos criar nossa coleção de exemplo foo no banco de dados dropTest . Vamos inserir 100 documentos sem um name campo e 100 documentos com um name idêntico campo para que possam ser removidos por engano mais tarde:
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"})};

Agora, vamos simular a remoção acidental de nossos 100 name documentos:
> db.foo.remove({ "name" : "some_x_name"})
WriteResult({ "nRemoved" : 100 })

Como estamos executando em um conjunto de réplicas, ainda temos um registro desses documentos no oplog (sendo inserido) e felizmente essas inserções (ainda) não caíram no final do oplog (o oplog é uma coleção limitada, lembre-se). Vamos ver se conseguimos encontrá-los:
use local;
db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}).count();
100

A contagem parece correta, parece que ainda temos nossos documentos. Eu sei por experiência que a única parte do oplog a entrada que precisaremos aqui é a o campo, então vamos adicionar uma projeção para retornar apenas isso (saída cortada por brevidade, mas você entendeu):
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" } }

Para reinserir esses documentos, podemos apenas armazená-los em um array, então iterar sobre o array e inserir as partes relevantes. Primeiro, vamos criar nosso array:
var deletedDocs = db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}, {"o" : 1}).toArray();
> deletedDocs.length
100

Em seguida, lembramos que temos apenas 100 documentos na coleção agora, então fazemos um loop sobre as 100 inserções e, finalmente, revalidamos nossas contagens:
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 aí está, com algumas ressalvas:
  • Isso não pretende ser uma verdadeira estratégia de restauração, veja os backups (MMS, outros), secundários atrasados ​​para isso, conforme mencionado nos comentários
  • Não será particularmente rápido consultar os documentos fora do oplog (qualquer consulta do oplog é uma verificação de tabela) em um sistema grande e ocupado.
  • Os documentos podem sair do oplog a qualquer momento (você pode, é claro, fazer uma cópia do oplog para uso posterior para ter mais tempo)
  • Dependendo da sua carga de trabalho, talvez seja necessário desduplicar os resultados antes de reinseri-los
  • Conjuntos maiores de documentos serão muito grandes para uma matriz, conforme demonstrado, portanto, você precisará iterar sobre um cursor
  • O formato do oplog é considerado interno e pode mudar a qualquer momento (sem aviso), então use por sua conta e risco