Pytanie Czy istnieje sposób na odzyskanie ostatnio usuniętych dokumentów w MongoDB?


Przez pomyłkę usunąłem niektóre dokumenty z mojego ostatniego zapytania. Czy istnieje sposób na przywrócenie mojej ostatniej kolekcji mongo.

Oto moje ostatnie zapytanie:

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

Czy istnieje opcja przywrócenia / cofnięcia? Czy mogę odzyskać dane?


14
2017-09-12 07:15


pochodzenie


Nie w tym przypadku ważne jest, aby zawsze wykonywać regularne kopie zapasowe, jeśli dokonujesz ważnych danych; dotyczy to każdej technologii baz danych - Sammaye
Kopie zapasowe zawsze są dobrym pomysłem, ale możesz też uruchomić replikę z opóźnieniem, aby dać ci możliwość poprawienia błędów w niedawnej przeszłości, jak poniżej: docs.mongodb.org/manual/tutorial/... - Adam Comerford
W przypadku korzystania z wiadomości MMS można było przywrócić czas. Nie ma innego sposobu na wycofanie !!!! - vmr


Odpowiedzi:


Nie ma opcji wycofania (rollback ma inne znaczenie w kontekście MongoDB), a ściśle mówiąc nie ma obsługiwanego sposobu na odzyskanie tych dokumentów - środki ostrożności, które możesz / powinny podjąć, są uwzględnione w komentarzach. Z tym jednak powiedziawszy, jeśli używasz zestawu replik, nawet zestawu replik jednego węzła, to masz oplog. Z oplog która obejmuje moment włożenia dokumentów, możesz je odzyskać.

Najprostszym sposobem zilustrowania tego jest przykład. Będę używał uproszczonego przykładu z zaledwie 100 skasowanymi dokumentami, które należy przywrócić. Aby wyjść poza to (ogromna liczba dokumentów, lub może chcesz tylko selektywnie przywrócić itd.) Będziesz albo chciał zmienić kod, aby powtórzyć kursor nad kursorem, albo napisać to za pomocą wybranego języka poza powłoką MongoDB. Podstawowa logika pozostaje taka sama.

Najpierw utwórzmy naszą przykładową kolekcję foo w bazie danych dropTest. Wstawimy 100 dokumentów bez name pole i 100 dokumentów z identycznym name pole, 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 zasymulujmy przypadkowe usunięcie naszych 100 name dokumenty:

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

Ponieważ uruchamiamy zestaw replik, wciąż posiadamy zapis tych dokumentów w pliku oplog (włożona) i na szczęście te wstawki nie (jeszcze) spadły z końca oplog ( oplog jest ograniczona kolekcja Zapamię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

Liczenie wygląda poprawnie, wydaje się, że wciąż mamy nasze dokumenty. Z doświadczenia wiem, że to jedyny kawałek oplog wejście, którego będziemy potrzebować, to o pole, dodajmy więc projekcję, aby tylko ją zwrócił (dane wyjściowe są przecinane 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 powtórzyć po tablicy i wstawić odpowiednie elementy. Najpierw utwórzmy naszą tablicę:

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 z kolekcji, a następnie zapętlamy 100 wstawek i na koniec potwierdzamy nasze dane:

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 tam to masz, z pewnymi zastrzeżeniami:

  • To nie jest prawdziwa strategia przywracania, przeglądaj kopie zapasowe (MMS, inne), opóźnione części zapasowe, jak wspomniano w komentarzach
  • Nie będzie szczególnie szybkie wysyłanie zapytań do dokumentów z oploga (dowolne zapytanie o oplog jest skanem tabeli) w dużym, zajętym systemie.
  • Dokumenty mogą w każdej chwili zestarzeć się z oploga (możesz oczywiście zrobić kopię oploga, aby później użyć, aby dać ci więcej czasu)
  • W zależności od obciążenia może być konieczne anulowanie wyników przed ponownym wstawieniem
  • Większe zestawy dokumentów będą zbyt duże dla tablicy, jak pokazano, więc będziesz musiał zamiast tego powtórzyć kursor
  • Format oplog jest uważany za wewnętrzny i może ulec zmianie w dowolnym momencie (bez powiadomienia), więc używaj go na własne ryzyko

18
2017-09-12 13:44





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

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

Zrobiłem eksplorację jednej opcji, która jest na znacznie niższym poziomie i może wymagać dostrojenia w oparciu o wersję MongoDB. Zrozumiałe jest, że jest zbyt niski poziom dla większości ludzi, ale działa i może być przydatny, gdy wszystko inne zawiedzie.

Moje podejście obejmuje bezpośrednią pracę z plikiem binarnym w pliku i używanie skryptu Python (lub poleceń) do identyfikowania, odczytu i rozpakowywania (BSON) usuniętych danych.

Moje podejście jest inspirowane przez to Projekt GitHub (NIE jestem twórcą tego projektu). Tutaj na moim blogu Próbowałem uprościć skrypt i wyodrębnić określony usunięty rekord z pliku Raw MongoDB.

Obecnie rekord jest oznaczony 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′

Zastąpiłem pierwszy blok rozmiarem rekordu, który wcześniej zidentyfikowałem na podstawie innych rekordów.

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

W końcu używając pakietu BSON (dostarczanego z pymongo), dekodowałem plik binarny do obiektu Readable.

bson.decode_all(y)

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

To BSON jest teraz obiektem python i może być porzucone do kolekcji odzyskiwania lub po prostu gdzieś zalogowany.

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


5
2017-07-18 01:59



Dzięki za odpowiedź! Wydaje się to rzadką informacją. Byłoby naprawdę pomocne !!. - trex
Witam - Byłem częścią pracy, którą piszę, w której również analizowałem, jak zrobić coś podobnego ze zmiennymi stopniami sukcesu z Apache Cassandrą i Apache HBase. - Yazad Khambata
Miałem bardzo duży zrzut danych do odzyskania, a pierwsza z powyższych łączy nie obsługuje usuniętych rekordów na Mongo 2.4, tak jak zrobiłem to @JazadKhambata. Więc przepisałem scenariusz w punkcie z informacją Yazada i otrzymałem to: gist.github.com/guss77/f8e610cfddbe02c07896 . Użyłem tego do odzyskania wielu tysięcy rekordów z dużej usuniętej kolekcji. - Guss