2015-11-08 76 views
2

我想更有效地更新大量(> 100,000)文檔。如何最有效地更新MongoDB中的大量文檔?

我的第一個幼稚的方法是在JS級別上執行它,編寫腳本,首先獲取_ids,然後循環_ids並通過_id調用更新(完整 文檔或$ set修補程序)。

我遇到了內存問題,也將數據分割成最大塊。 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%,因此可能會出現一些無法正確報告正確數字的邊緣情況。

對於你的三個常見的使用情況,您可以實現這樣的大宗原料藥:

案例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(); 
     }); 
    } 
}); 

案例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,錯過了大宗原料藥 – ezmilhouse

+1

建議加快代碼:(1)使用有限的投影上的搜索查詢,如果你只需要輕按一個領域,設置投影對於這一領域而言,這將加速在線上提供文檔。 (2)正如@chridam所提到的,使用UnorderedBulkOp並行更新,所以速度更快。 – marmor