Как обрабатывать изменение схемы mongodb в производстве

Я использую бэкенд ORM mongodb + node.js + mongoose.js.

Скажем, у меня есть вложенный массив объектов без поля _id

mongoose.Schema({
  nested: [{
    _id: false, prop: 'string'
  }]
})

И затем я хочу добавить поле _id ко всем вложенным объектам, поэтому схема мангуста будет

mongoose.Schema({
  nested: [{
    prop: 'string'
  }]
})

Затем я должен запустить какой-нибудь скрипт для изменения рабочей БД, верно? Как лучше всего справиться с такими изменениями? Какой инструмент (или подход) лучше всего использовать для реализации изменений?


person WHITECOLOR    schedule 12.01.2013    source источник
comment
Из примера, который вы привели, похоже, что вы хотите удалить _id вместо его добавления. Если вы хотите добавить _id, как определить, каким должен быть каждый _id?   -  person Eduardo    schedule 12.01.2013
comment
Я не понимаю тебя. _id: false указывает mongoose не генерировать _id для объектов, описанных схемой, если я удалю _id: false из описания схемы, mongoose создаст новые документы с сгенерированным _id. То, что я прошу, - это правильный способ заполнить все существующие объекты (у которых нет _id) новыми _ids.   -  person WHITECOLOR    schedule 12.01.2013
comment
должны ли _ids генерироваться системой или вами?   -  person Eduardo    schedule 12.01.2013
comment
Я думаю по системе. У меня нет никаких идентификаторов, мне просто нужно, чтобы все объекты имели _ids.   -  person WHITECOLOR    schedule 12.01.2013
comment
Вам нужно написать код, чтобы сделать это вручную, элемент за элементом.   -  person JohnnyHK    schedule 12.01.2013
comment
Как лучше всего запустить этот код? Сделать автономный модуль node.js, который подключается к БД и вносит изменения? Каков правильный подход к внедрению изменений?   -  person WHITECOLOR    schedule 12.01.2013
comment
Лучший метод здесь, поскольку кажется, что одноразовая команда может быть либо, как вы говорите, модулем node.js, либо скриптом js, запускаемым в консоли MongoDB на мастере.   -  person Sammaye    schedule 13.01.2013
comment
Может быть, я что-то упускаю, но MongoDb — это база данных без схемы. Коллекции не имеют определенной схемы, каждый объект, хранящийся в коллекции, имеет свою схему в самом объекте. Сохранение объекта обновит его схему, но, возможно, я упустил вашу мысль (не уверен, что вы имели в виду под изменением рабочей БД).   -  person Marquez    schedule 20.01.2013
comment
mongoose.js вводит твердые схемы. Кроме того, я бы прочитал без схемы как с гибкой структурой. Вам нужна своего рода схема для любой структуры данных, как вам нужна карта в неизвестном городе. Под изменением производственной базы данных я подразумеваю, например, если вы решите удалить какое-либо поле из всех документов в коллекции.   -  person WHITECOLOR    schedule 21.01.2013
comment
просто плюс: я бы добавил schema_version в документ, чтобы отслеживать изменения схемы   -  person boj    schedule 25.09.2015


Ответы (3)


Одним из существенных преимуществ баз данных без схемы является то, что вам не нужно обновлять всю базу данных с помощью новых макетов схемы. Если некоторые из документов в БД не содержат конкретной информации, тогда ваш код может вместо этого сделать соответствующую вещь или решить сделать что-нибудь с этой записью.

Другой вариант — лениво обновлять документы по мере необходимости — только при повторном просмотре. В этом случае вы можете выбрать флаг версии для каждой записи/документа, который изначально может даже не отображаться (и, таким образом, означать «версию 0»). Хотя даже это необязательно. Вместо этого ваш код доступа к базе данных ищет необходимые ему данные, и если они не существуют, поскольку это новая информация, добавленная после обновления кода, то он заполнит результаты в меру своих возможностей.

В вашем примере преобразование _id:false в стандартное поле MongoId, когда код читается (или записывается обратно после обновления), а _id:false в настоящее время установлено, затем внесите изменение и запишите его только тогда, когда это абсолютно необходимо.

person Alister Bulman    schedule 21.01.2013
comment
Извините, я не понимаю, что вы имеете в виду под _id:false. Я действительно заинтересован. Можешь объяснить, пожалуйста? - person hgoebl; 25.03.2014
comment
Ах, я не читал текст вопроса, извините, это не ваша вина. Но пример с _id:false может немного ввести в заблуждение относительно всего вопроса. Было бы неплохо иметь пример, более понятный для всех, особенно для тех, кто не использует Mongoose. - person hgoebl; 25.03.2014
comment
Как это будет с такими операциями, как добавление нового индекса: patientSchema.index({ patientId: 1, institute: 1}, { unique: true }), в dev мне пришлось удалить старый индекс без { unique: true }, чтобы он заработал - person Andi Giga; 26.01.2016

Вам действительно нужно написать скрипт, который будет просматривать коллекцию и добавлять новое поле в каждый документ. Однако точный способ, как вы это сделаете, зависит от размера вашей БД и производительности вашей системы хранения. Добавление поля в документ изменит его размер и, таким образом, в большинстве случаев приведет к перемещению. Эта операция влияет на IO и также ограничена им. Если ваша коллекция состоит всего из нескольких тысяч документов, может быть до ста тысяч, то вы можете просто перебрать ее в одном цикле, потому что вся коллекция, вероятно, помещается в память, и все операции ввода-вывода будут происходить позже. Однако, если коллекция выходит далеко за пределы доступной памяти, подход усложняется. Обычно мы следуем следующим шагам при использовании MongoDB в производственной среде:

  • Открыть курсор с тайм-аутом = False
  • Чтение фрагмента документов в память
  • Выполнять запросы на обновление этих документов
  • Переждите некоторое время, чтобы избежать перегрузки подсистемы ввода-вывода и повреждения производственного приложения.
  • Повторяйте, пока не закончите
  • Закрыть курсор :)

Размер блока документов и период ожидания должны быть определены экспериментально. Обычно вы хотите избежать QR/QW в mongostats на период миграции. Для больших коллекций на более медленных дисках (например, EBS на Amazon) этот подход, обеспечивающий безопасность ввода-вывода, может занять от нескольких часов до нескольких дней.

person Michael Korbakov    schedule 21.01.2013
comment
У вас есть пример короткого кода для курсора? Меня особенно интересует версия JavaScript, потому что я думаю, что это не тривиально, особенно спать некоторое время и не получать параллель... - person hgoebl; 25.03.2014
comment
У меня нет примера для JavaScript, но в драйвере PyMongo отключение тайм-аута для курсора выполняется путем простой передачи timeout=False в метод find(). Я думаю, что драйвер JavaScript будет иметь что-то вроде этого. - person Michael Korbakov; 01.04.2014
comment
В таком случае могу ли я продолжать использовать схемы Mongoose? Я спрашиваю об этом, потому что схемы Mongoose автоматически обновляются, мы всегда обновляем структуру схемы. - person Francis Rodrigues; 11.09.2018

Расширяя ответ @Michael Korbakov, я реализовал его шаги с помощью сценария оболочки mongo (см. Справочное руководство MongoDB о mongo сценариях оболочки).

Важно: как указано в Справочное руководство по MongoDB, запуск сценария в оболочке mongo может повысить производительность, поскольку он уменьшает задержку соединения для каждой пакетной выборки и массового выполнения.

Недостатком, который следует учитывать, является то, что команды оболочки mongo всегда синхронны, но массовое выполнение уже заботится о параллелизме (для каждого фрагмента), поэтому мы хороши для этого варианта использования.

код:

// constants
var sourceDbName = 'sourceDb';
var sourceCollectionName = 'sourceColl';
var destDbName = 'destdb';
var destCollectionName = 'destColl';
var bulkWriteChunckSize = 1000;
// for fetching, I figured 1000 for current bulkWrite, and +1000 ready for next bulkWrite
var batchSize = 2000;    
var sourceDb = db.getSiblingDB(sourceDbName);
var destDb = db.getSiblingDB(destDbName);

var start = new Date();

var cursor = sourceDb[sourceCollectionName].find({}).noCursorTimeout().batchSize(batchSize);

var currChunkSize = 0;
var bulk = destDb[destCollectionName].initializeUnorderedBulkOp();
cursor.forEach(function(doc) {
    currChunkSize++;
    bulk.insert({
        ...doc,
        newProperty: 'hello!',
    }); // can be changed for your need, if you want update instead

    if (currChunkSize === bulkWriteChunckSize) {
        bulk.execute();

        // each bulk.execute took for me 130ms, so i figured to wait the same time as well
        sleep(130);

        currChunkSize = 0;
        bulk = destDb[destCollectionName].initializeUnorderedBulkOp();
    }
});

if (currChunkSize > 0) {
    bulk.execute();
    currChunkSize = 0;
}

var end = new Date();
print(end - start);

cursor.close();
person noam7700    schedule 31.07.2020