2015-11-08 4 views
2

Я хочу обновить большое количество (> 100 000) документов наиболее эффективно.Как обновить большое количество документов в MongoDB наиболее эффективно?

Мой первый наивный подход делает это на уровне JS, написание сценариев, Fetch _ids первым, затем петлю через _ids и вызывать обновлений _id (полные документы или $ набор патчей).

Я столкнулся с проблемами с памятью, а также передал данные в куски макс. 500 документы (с открытием и закрытием соединения), похоже, не работают хорошо.

Итак, как я могу решить эту проблему на уровне MongoDB?
Лучшая практика?

У меня есть 3 случая общего пользования, как правило, работы по техническому обслуживанию потоков:

1. Изменить тип стоимости имущества, не меняя значения.

// before 
 
{ 
 
    timestamp : '1446987395' 
 
} 
 

 
// after 
 
{ 
 
    timestamp : 1446987395 
 
}

2. Добавить новое свойство на основе стоимости имеющегося имущества.

// before 
 
{ 
 
    firstname : 'John', 
 
    lastname : 'Doe' 
 
} 
 

 
// after 
 
{ 
 
    firstname : 'John', 
 
    lastname : 'Doe', 
 
    name  : 'John Doe' 
 
}

3. Простое добавление удаление свойства из документов.

// before 
 
{ 
 
    street : 'Whatever Ave', 
 
    street_no : '1025' 
 
} 
 

 
// after 
 
{ 
 
    street : 'Whatever Ave', 
 
    no  : '1025' 
 
}

Спасибо за помощь.

ответ

5

Если ваш сервер MongoDB является 2,6 или более поздней версии, было бы лучше воспользоваться помощью команды записи Bulk API, которые позволяют для выполнения объемных update операции, которые просто абстракции поверх сервера, чтобы сделать легко создавать массовые операции. Эти массовые операции поступают в основном в двух вариантах:

  • упорядоченные массовые операции. Эти операции выполняют всю операцию в порядке и ошибки при первой ошибке записи.
  • Неупорядоченные массовые операции. Эти операции выполняют все операции параллельно и суммируют все ошибки. Неупорядоченные массовые операции не гарантируют порядок исполнения.

Обратите внимание, что для более старых серверов, чем 2.6, API будет преобразовывать данные из строя. Однако невозможно понизить 100% -ное преобразование, так что могут быть случаи с краем, когда он не может правильно сообщать правильные номера.

Для ваших трех случаев общего пользования, вы могли бы реализовать Bulk API, как это:

Случай 1. Изменить тип стоимости имущества, без изменения значения:

var MongoClient = require('mongodb').MongoClient; 

MongoClient.connect("mongodb://localhost:27017/test", function(err, db) { 
    // Handle error 
    if(err) throw err; 

    // Get the collection and bulk api artefacts 
    var col = db.collection('users'),   
     bulk = col.initializeOrderedBulkOp(), // Initialize the Ordered Batch 
     counter = 0;   

    // Case 1. Change type of value of property, without changing the value.   
    col.find({"timestamp": {"$exists": true, "$type": 2} }).each(function (err, doc) { 

     var newTimestamp = parseInt(doc.timestamp); 
     bulk.find({ "_id": doc._id }).updateOne({ 
      "$set": { "timestamp": newTimestamp } 
     }); 

     counter++; 

     if (counter % 1000 == 0) { 
      bulk.execute(function(err, result) { 
       // re-initialise batch operation   
       bulk = col.initializeOrderedBulkOp(); 
      }); 
     } 
    }); 

    if (counter % 1000 != 0){ 
     bulk.execute(function(err, result) { 
      // do something with result 
      db.close(); 
     }); 
    } 
}); 

Case 2. Добавить новое свойство на основе значения существующего свойства:

MongoClient.connect("mongodb://localhost:27017/test", function(err, db) { 
    // Handle error 
    if(err) throw err; 

    // Get the collection and bulk api artefacts 
    var col = db.collection('users'),   
     bulk = col.initializeOrderedBulkOp(), // Initialize the Ordered Batch 
     counter = 0;   

    // Case 2. Add new property based on value of existing property.   
    col.find({"name": {"$exists": false } }).each(function (err, doc) { 

     var fullName = doc.firstname + " " doc.lastname; 
     bulk.find({ "_id": doc._id }).updateOne({ 
      "$set": { "name": fullName } 
     }); 

     counter++; 

     if (counter % 1000 == 0) { 
      bulk.execute(function(err, result) { 
       // re-initialise batch operation   
       bulk = col.initializeOrderedBulkOp(); 
      }); 
     } 
    }); 

    if (counter % 1000 != 0){ 
     bulk.execute(function(err, result) { 
      // do something with result 
      db.close(); 
     }); 
    } 
}); 

Дело 3. Просто добавление удаляемых свойств из документов.

MongoClient.connect("mongodb://localhost:27017/test", function(err, db) { 
    // Handle error 
    if(err) throw err; 

    // Get the collection and bulk api artefacts 
    var col = db.collection('users'),   
     bulk = col.initializeOrderedBulkOp(), // Initialize the Ordered Batch 
     counter = 0;   

    // Case 3. Simply adding removing properties from documents.  
    col.find({"street_no": {"$exists": true } }).each(function (err, doc) { 

     bulk.find({ "_id": doc._id }).updateOne({ 
      "$set": { "no": doc.street_no }, 
      "$unset": { "street_no": "" } 
     }); 

     counter++; 

     if (counter % 1000 == 0) { 
      bulk.execute(function(err, result) { 
       // re-initialise batch operation   
       bulk = col.initializeOrderedBulkOp(); 
      }); 
     } 
    }); 

    if (counter % 1000 != 0){ 
     bulk.execute(function(err, result) { 
      // do something with result 
      db.close(); 
     }); 
    } 
}); 
+1

совершенно, спасибо chridam, пропустили на насыпной API – ezmilhouse

+1

предложений для ускорения кода: (1) использовать ограниченную проекцию на запросе находки, если вам нужно только прикоснуться к одному полю, установите проекцию только для этого поля это ускорит доставку документов на проводе. (2) Как упоминалось в @chridam с использованием обновлений UnorderedBulkOp параллельно, так что это намного быстрее. – marmor

Смежные вопросы