Подтвердить что ты не робот

MongoDB - какой самый быстрый способ обновить все записи в коллекции?

У меня есть коллекция с 9 миллионами записей. В настоящее время я использую следующий script для обновления всей коллекции:

simple_update.js

db.mydata.find().forEach(function(data) {
  db.mydata.update({_id:data._id},{$set:{pid:(2571 - data.Y + (data.X * 2572))}});
});

Это выполняется из командной строки следующим образом:

mongo my_test simple_update.js

Итак, все, что я делаю, это добавление нового поля pid на основе простого вычисления.

Есть ли более быстрый способ? Это занимает значительное количество времени.

4b9b3361

Ответ 1

Есть две вещи, которые вы можете сделать.

Эта ссылка также содержит следующий совет:

Это хороший метод для выполнения административной работы. Запустите mongo на сервере, подключившись через интерфейс localhost. Соединение происходит очень быстро и с низкой задержкой. Это более дружелюбно, чем db.eval(), поскольку db.eval() блокирует другие операции.

Это, вероятно, самый быстрый, который вы получите. Вы должны понимать, что выпуск обновлений 9M на одном сервере будет тяжелой операцией. Скажем, что вы можете получить 3k обновлений в секунду, вы все еще говорите о работе около часа.

И это не проблема "монго", которая будет ограничена аппаратным обеспечением.

Ответ 2

Я использую метод db.collection.update

// db.collection.update( criteria, objNew, upsert, multi ) // --> for reference
db.collection.update( { "_id" : { $exists : true } }, objNew, upsert, true);

Ответ 3

Я не буду рекомендовать использовать {multi: true} для большого набора данных, потому что он менее настраиваемый.

Лучший способ с использованием объемной вставки.

Массовая операция действительно полезна для задач планировщика. Скажем, вы должны удалять данные старше 6 месяцев в день. Используйте массовую операцию. Это быстро и не замедляет работу сервера. Процессор, использование памяти не заметно, когда вы вставляете, удаляете или обновляете более миллиарда документов. Я обнаружил, что {multi: true} замедляет работу сервера, когда вы имеете дело с документами million+ (для этого нужно больше исследований).

Смотрите образец ниже. Это сценарий оболочки js, который может запускаться на сервере в качестве программы узла (для этого используйте npm-модуль shelljs или аналогичный)

обновить монго до 3. 2+

Обычный способ обновления нескольких уникальных документов

let counter = 0;
db.myCol.find({}).sort({$natural:1}).limit(1000000).forEach(function(document){
    counter++;
    document.test_value = "just testing" + counter
    db.myCol.save(document)
});

Прошло 310-315 секунд, когда я попробовал. Это более 5 минут на обновление миллиона документов.

Моя коллекция включает в себя 100 документов million+, поэтому скорость может отличаться для других.

То же самое, используя массовую вставку

    let counter = 0;
// magic no.- depends on your hardware and document size. - my document size is around 1.5kb-2kb
// performance reduces when this limit is not in 1500-2500 range.
// try different range and find fastest bulk limit for your document size or take an average.
let limitNo = 2222; 
let bulk = db.myCol.initializeUnorderedBulkOp();
let noOfDocsToProcess = 1000000;
db.myCol.find({}).sort({$natural:1}).limit(noOfDocsToProcess).forEach(function(document){
    counter++;
    noOfDocsToProcess --;
    limitNo--;
    bulk.find({_id:document._id}).update({$set:{test_value : "just testing .. " + counter}});
    if(limitNo === 0 || noOfDocsToProcess === 0){
        bulk.execute();
        bulk = db.myCol.initializeUnorderedBulkOp();
        limitNo = 2222;
    }
});

Лучшее время было 8972 миллис. Таким образом, в среднем потребовалось всего 10 секунд, чтобы обновить миллион документов. В 30 раз быстрее старого.

Поместите код в файл .js и выполните как скрипт оболочки mongo.

Если кто-то нашел лучший способ, пожалуйста, обновите. Позволяет использовать монго быстрее.

Ответ 4

Не уверен, что это будет быстрее, но вы можете сделать многореактивное обновление. Просто скажите update where _id > 0 (это будет верно для каждого объекта), а затем установите флаг "multi" в true, и он должен сделать то же самое, не выполняя итерацию всей коллекции.

Проверьте это: MongoDB - Выполнение бокового кода сервера

Ответ 5

Начиная с Mongo 4.2, db.collection.update() может принять конвейер агрегации, что в конечном итоге позволяет обновить/создать поле на основе другого поля; и, таким образом, позволяет нам полностью применить этот вид запросов на стороне сервера:

// { Y: 456,  X: 3 }
// { Y: 3452, X: 2 }
db.collection.update(
  {},
  [{ $set: { pid: {
    $sum: [ 2571, { $multiply: [ -1, "$Y" ] }, { $multiply: [ 2572, "$X" ] } ]
  }}}],
  { multi: true }
)
// { Y: 456,  X: 3, pid: 9831 }
// { Y: 3452, X: 2, pid: 4263 }
  • Первая часть {} - это запрос на совпадение, который фильтрует, какие документы обновлять (в данном случае все документы).

  • Вторая часть [{ $set: { pid:... } }] представляет собой конвейер агрегации обновлений (обратите внимание на квадратные скобки, обозначающие использование конвейера агрегации). $set - новый оператор агрегирования и псевдоним $addFields. Обратите внимание, как pid создается напрямую на основе значений X ($X) и Y ($Y) из одного и того же документа.

  • Не забудьте { multi: true }, иначе будет обновлен только первый соответствующий документ.