Der er ingen rollback-mulighed (rollback har en anden betydning i MongoDB-sammenhæng), og strengt taget er der ingen understøttet måde at få disse dokumenter tilbage på - de forholdsregler du kan/bør tage er dækket i kommentarerne. Men med det sagt, hvis du kører et replikasæt, selv et enkelt node replikasæt, så har du en oplog
. Med en oplog
der dækker, hvornår dokumenterne blev indsat, kan du muligvis gendanne dem.
Den nemmeste måde at illustrere dette på er med et eksempel. Jeg vil bruge et forenklet eksempel med kun 100 slettede dokumenter, der skal gendannes. For at gå ud over dette (enormt antal dokumenter, eller måske ønsker du kun selektivt at gendanne osv.) vil du enten ændre koden til at gentage over en markør eller skrive dette ved at bruge dit valgte sprog uden for MongoDB-skallen. Den grundlæggende logik forbliver den samme.
Lad os først oprette vores eksempelsamling foo
i databasen dropTest
. Vi indsætter 100 dokumenter uden et name
felt og 100 dokumenter med et identisk name
felt, så de ved en fejl kan fjernes senere:
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"})};
Lad os nu simulere den utilsigtede fjernelse af vores 100 name
dokumenter:
> db.foo.remove({ "name" : "some_x_name"})
WriteResult({ "nRemoved" : 100 })
Fordi vi kører i et replikasæt, har vi stadig en registrering af disse dokumenter i oplog
(ved at blive indsat), og heldigvis er disse indsættelser (endnu) ikke faldet ud af slutningen af oplog
(oplog
er en lukket samling husk). Lad os se, om vi kan finde dem:
use local;
db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}).count();
100
Optællingen ser korrekt ud, vi ser ud til at have vores dokumenter stadig. Jeg ved af erfaring, at den eneste del af oplog
indgang vi skal bruge her er o
felt, så lad os tilføje en projektion for kun at returnere det (output klippet for kortheds skyld, men du forstår ideen):
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" } }
For at genindsætte disse dokumenter kan vi bare gemme dem i et array, derefter iterere over arrayet og indsætte de relevante stykker. Lad os først oprette vores array:
var deletedDocs = db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}, {"o" : 1}).toArray();
> deletedDocs.length
100
Dernæst minder vi os selv om, at vi kun har 100 dokumenter i samlingen nu, og derefter går vi over de 100 indstik og genvaliderer til sidst vores optælling:
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
Og der har du det, med nogle forbehold:
- Dette er ikke ment som en ægte gendannelsesstrategi, se sikkerhedskopier (MMS, andet), forsinkede sekundære for det, som nævnt i kommentarerne
- Det vil ikke være særlig hurtigt at forespørge dokumenterne ud af oploggen (enhver oplog-forespørgsel er en tabelscanning) på et stort travlt system.
- Dokumenterne kan til enhver tid ældes ud af oploggen (du kan selvfølgelig lave en kopi af oploggen til senere brug for at give dig mere tid)
- Afhængigt af din arbejdsbyrde skal du muligvis de-dupere resultaterne, før du genindsætter dem
- Større sæt dokumenter vil være for store til en matrix som vist, så du bliver nødt til at iterere over en markør i stedet for
- Formatet af
oplog
betragtes som intern og kan ændres til enhver tid (uden varsel), så brug på eget ansvar