Czy jest jakiś sposób na odzyskanie ostatnio usuniętych dokumentów w MongoDB?

Usunąłem niektóre dokumenty w moim ostatnim zapytaniu przez pomyłkę, czy jest jakiś sposób, aby cofnąć moje ostatnie zapytanie kolekcja mongo.

Oto moje ostatnie zapytanie:

 db.datas.remove({ "name" : "some_x_name"}) 

Czy jest jakaś opcja cofania/cofania? Czy mogę odzyskać moje dane?

Author: Adam Comerford, 2014-09-12

2 answers

Nie ma opcji wycofywania (wycofywanie ma inne znaczenie w kontekście MongoDB) i ściśle mówiąc nie ma obsługiwanego sposobu odzyskania tych dokumentów - środki ostrożności, które możesz/powinieneś podjąć, są omówione w komentarzach. Z tym jednak, jeśli uruchamiasz zestaw replik, nawet zestaw replik z pojedynczym węzłem, to masz oplog. Z oplog, który obejmuje kiedy dokumenty zostały wstawione, możesz być w stanie je odzyskać.

Najprostszy sposób aby zilustrować to na przykładzie. Użyję uproszczonego przykładu z zaledwie 100 usuniętych dokumentów, które należy przywrócić. Aby wyjść poza to (ogromna liczba dokumentów, a może chcesz tylko selektywnie przywrócić itp.) albo będziesz chciał zmienić kod tak, aby powtarzał się nad kursorem, albo napisz go używając wybranego języka poza powłoką MongoDB. Podstawowa logika pozostaje taka sama.

Najpierw stwórzmy naszą przykładową kolekcję foo w bazie danych dropTest. Będziemy Wstaw 100 dokumentów bez pola name i 100 dokumentów z identycznym polem name, aby można je później omyłkowo usunąć:

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"})};
Teraz symulujmy przypadkowe usunięcie naszych 100 dokumentów:]}
> db.foo.remove({ "name" : "some_x_name"})
WriteResult({ "nRemoved" : 100 })

Ponieważ biegamy w zestawie replik, wciąż mamy zapis tych dokumentów w oplog (wstawiamy) i na szczęście te wstawki nie spadły (jeszcze) z końca oplog (oplog jest } zbiorem zamkniętym pamiętaj). Zobaczmy, czy możemy je znaleźć:

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

Hrabia wygląda poprawnie, wydaje się, że mamy nasze dokumenty nadal. Z doświadczenia wiem, że jedynym fragmentem wpisu oplog, którego będziemy potrzebować, jest pole o, więc dodajmy rzut, aby zwrócić tylko to (wyjście wycięte dla zwięzłości, ale masz pomysł): {]}

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

Aby ponownie wstawić te dokumenty, możemy po prostu zapisać je w tablicy, a następnie iterację nad tablicą i wstawić odpowiednie elementy. Najpierw stwórzmy naszą tablica:

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

Następnie przypominamy sobie, że mamy teraz tylko 100 dokumentów w kolekcji, a następnie pętlę nad 100 wstawkami i na koniec poprawiamy liczbę:

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

I masz to, z pewnymi zastrzeżeniami:

    Nie ma to być prawdziwa strategia przywracania, spójrz na kopie zapasowe( MMS, inne), opóźnione wtórniki, jak wspomniano w komentarzach [45]}
  • nie będzie szczególnie szybko odpytywać dokumentów z oploga (dowolnego oploga zapytanie jest skanowaniem tabeli) na dużym zajętym systemie.
  • W 2004 roku, w ramach projektu oplog, w ramach projektu oplog, w ramach projektu oplog, w 2006 roku, w ramach projektu oplog, w 2007 roku, w ramach projektu oplog, w 2009 roku, w ramach projektu oplog, w 2009 roku, w 2009 roku, w 2009 roku, w 2009 roku]}
  • W zależności od nakładu pracy możesz być zmuszony do odtajnienia wyników przed ponownym włożeniem ich
  • większe zestawy dokumentów będą zbyt duże dla tablicy, jak pokazano, więc będziesz musiał iterację nad kursorem
  • format oplog jest uważany za wewnętrzny i może zmiana w dowolnym momencie (bez powiadomienia), więc używaj na własne ryzyko]}
 19
Author: Adam Comerford,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-12 15:22:06

Chociaż rozumiem, że jest to trochę stare, ale chciałem podzielić się czymś, co zbadałem w tej dziedzinie, które mogą być przydatne dla innych z podobnym problemem.

Faktem jest, że MongoDB nie fizycznie usuwa danych natychmiast - oznacza je tylko do usunięcia. Jest to jednak specyficzna wersja i obecnie nie ma dokumentacji ani standaryzacji - co mogłoby umożliwić zewnętrznemu twórcy narzędzi (lub komuś w desperackiej potrzebie) zbudowanie narzędzia lub napisanie prostego skryptu niezawodnie to działa w różnych wersjach. Otworzyłem bilet na to - https://jira.mongodb.org/browse/DOCS-5151 .

Zbadałem jedną z opcji, która jest na znacznie niższym poziomie i może wymagać dokładnego dostrojenia w oparciu o używaną wersję MongoDB. Co zrozumiałe, zbyt niski poziom dla większości linkowania, jednak działa i może być przydatny, gdy Wszystko inne zawiedzie.

Moje podejście polega na bezpośredniej pracy z plikiem binarnym i użyciu skryptu Pythona (lub poleceń) do identyfikacji, odczytu i rozpakowania (BSON) usunięte dane.

Moje podejście jest inspirowane tym projektem GitHub (nie jestem jego twórcą). tutaj na moim blogu próbowałem uprościć skrypt i wyodrębnić konkretny usunięty rekord z surowego pliku MongoDB.

Obecnie rekord jest oznaczany do usunięcia jako "\xee " na początku rekordu. Tak wygląda usunięty rekord w surowym pliku 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′

Pierwszy blok zamieniłem na rozmiar rekordu, który zidentyfikowane wcześniej na podstawie innych zapisów.

y=”3\x00\x00\x00″+x[20804:20800+51]

W końcu używając pakietu BSON (który jest dostarczany z pymongo), zdekodowałem plik binarny do czytelnego obiektu.

bson.decode_all(y)

[{u’_id': ObjectId(‘5519a6679fdf19c1ad73dba8′), u’name': u’AAA’, u’marks': 2000.0}]

Ten BSON jest teraz obiektem Pythona i może zostać wrzucony do kolekcji recover lub po prostu gdzieś zalogowany.

Nie trzeba dodawać, że ta lub jakakolwiek inna technika odzyskiwania powinna być najlepiej wykonana w obszarze przejściowym na kopii zapasowej pliku bazy danych.

 5
Author: Yazad Khambata,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-18 02:04:05