Ответ 1
Нет опции отката (откат имеет другое значение в контексте MongoDB), и, строго говоря, нет способа поддержать эти документы - меры предосторожности вы можете/должны принять, рассматриваются в комментариях. С учетом сказанного, однако, если вы используете набор реплик, даже один набор реплик node, то у вас есть oplog
. С oplog
, который будет закрыт при вставке документов, вы сможете восстановить их.
Самый простой способ проиллюстрировать это - пример. Я использую упрощенный пример с только 100 удаленными документами, которые необходимо восстановить. Чтобы выйти за рамки этого (огромное количество документов или, возможно, вы хотите только выборочно восстановить и т.д.), Вы либо захотите изменить код, чтобы перебрать курсор, либо написать его, используя свой язык выбора вне оболочки MongoDB. Основная логика остается прежней.
Сначала создайте наш пример коллекции foo
в базе данных dropTest
. Мы вставим 100 документов без поля name
и 100 документов с идентичным полем name
, чтобы их можно было по ошибке удалить позже:
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"})};
Теперь давайте смоделируем случайное удаление наших 100 name
документов:
> db.foo.remove({ "name" : "some_x_name"})
WriteResult({ "nRemoved" : 100 })
Поскольку мы работаем в наборе реплик, у нас все еще есть запись этих документов в oplog
(вставляется), и, к счастью, эти вставки еще не упали с конца oplog
(oplog
является закрытой коллекцией). Посмотрим, найдем ли мы их:
use local;
db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}).count();
100
Счет выглядит правильно, у нас, похоже, все еще есть наши документы. Я знаю по опыту, что единственный фрагмент записи oplog
, который нам понадобится здесь, - это поле o
, поэтому добавьте проекцию только для возврата (вывод сокращен для краткости, но вы получите идею):
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" } }
Чтобы повторно вставить эти документы, мы можем просто сохранить их в массиве, затем перебрать массив и вставить соответствующие фрагменты. Сначала создайте наш массив:
var deletedDocs = db.oplog.rs.find({op : "i", ns : "dropTest.foo", "o.name" : "some_x_name"}, {"o" : 1}).toArray();
> deletedDocs.length
100
Затем мы напоминаем себе, что теперь у нас есть только 100 документов в коллекции, затем перебираем 100 вставок и, наконец, подтверждаем наши подсчеты:
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
И у вас есть это, с некоторыми оговорками:
- Это не значит, что это настоящая стратегия восстановления, посмотрите на резервные копии (MMS, другие), отсроченные второстепенные для этого, как указано в комментариях.
- Не будет особенно быстро запрашивать документы из oplog (любой запрос oplog - это сканирование таблицы) в большой загруженной системе.
- В любой момент документы могут возрасти из oplog (вы можете, конечно, сделать копию oplog для последующего использования, чтобы дать вам больше времени).
- В зависимости от вашей рабочей нагрузки вам, возможно, придется обнулить результаты перед их повторной установкой.
- Большие наборы документов будут слишком большими для массива, как показано, поэтому вам нужно будет перебирать курсор вместо
- Формат
oplog
считается внутренним и может меняться в любое время (без уведомления), поэтому используйте на свой страх и риск