MongoDB - какой самый быстрый способ обновить все записи в коллекции? - PullRequest
38 голосов
/ 10 ноября 2010

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

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 на основе простого вычисления.

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

Ответы [ 5 ]

28 голосов
/ 11 ноября 2010

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

  1. Отправить обновление с флагом 'multi', установленным в true.
  2. Сохраните функцию на стороне сервера и попробуйте использовать Выполнение кода на стороне сервера .

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

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

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

И это на самом деле не «проблема Монго», это будет аппаратное ограничение.

18 голосов
/ 24 января 2011

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

// db.collection.update( criteria, objNew, upsert, multi ) // --> for reference
db.collection.update( { "_id" : { $exists : true } }, objNew, upsert, true);
3 голосов
/ 09 июня 2018

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

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

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

См. Образец ниже. Это скрипт оболочки 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 миллионов документов, поэтому скорость может отличаться для других.

То же самое с использованием массовой вставки:

    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.

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

0 голосов
/ 12 июня 2019

Начиная с 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 }, иначе будет обновлен только первый соответствующий документ.

0 голосов
/ 10 ноября 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...