2014-09-03 61 views
1

我在mongoDB中的整個數據集(大約300萬個文檔)上運行以下查詢,以將字符串的用戶標識更改爲ints。該查詢似乎並沒有完成:將字符串更改爲mongoDB中的整數需要很長的時間

var cursor = db.play_sessions.find() 

while (cursor.hasNext()) { 
    var play = cursor.next(); 
    db.play_sessions.update({_id : play._id}, {$set : {user_id : new NumberInt(play.user_id) }}); 
} 

我運行在相同的數據集此查詢並比較迅速恢復:

db.play_sessions.find().forEach(function(play){ 
    if (play.score && play.user_id && play.user_attempt_no && play.game_id && play.level && play.training_session_id) { 
     print(play.score,",",play.user_id,",",play.user_attempt_no,",",play.game_id,",",play.level,",",parseInt(play.training_session_id).toFixed()); 
    } else if (play.score && play.user_id && play.user_attempt_no && play.game_id && play.level) { 
     print(play.score,",",play.user_id,",",play.user_attempt_no,",",play.game_id,",",play.level); 
    }; 
}); 

我明白我在第一個查詢寫入數據庫,但爲什麼第一個查詢似乎永遠不會返回,而第二個查詢卻相對較快呢?第一個查詢中的代碼有問題嗎?

回答

1

300萬份文件是相當多的文件,所以整個操作將需要一段時間。但這裏需要考慮的主要問題是,您要求將數據「發送」到數據庫,並「接收」300萬次確認的寫入響應(因爲這是發生的情況)。這僅僅是在操作之間等待,而不是簡單地迭代遊標。

這裏的另一個原因是很可能您正在運行MongoDB 2.6或更高版本。早期版本和版本之間的核心區別在於如何在shell中處理此代碼。其核心是Bulk Operations API,其中包含所有殼幫助程序實際使用的方法,用於與數據庫交互的全部

在之前的版本中,在這種「循環」操作中,對於每次迭代,「寫入關注」確認沒有在此上下文中完成。現在完成的方式(因爲幫助者實際上使用Bulk API),每次迭代都會返回確認。這會讓事情減慢很多。除非你直接使用批量操作。

所以要「重鑄」的現代版本的值,而是執行此操作:

var bulk = db.play_sessions.initializeUnorderedBulkOp(); 
var count = 0; 

db.play_sessions.find({ "user_id": { "$type": 2 } }).forEach(function(doc) { 
    bulk.find({ "_id": doc._id }).updateOne({ 
     "$set": { "user_id": NumberInt(doc.user_id) } 
    }); 
    count++; 

    if (count % 10000 == 0) { 
     bulk.execute(); 
     bulk = db.play_sessions.initializeUnorderedBulkOp(); 
    } 
}); 

if (count % 10000 != 0) 
    bulk.execute(); 

批量操作把所有的「批」的單一請求。實際上,底層驅動程序將其分解爲1000個項目的單個批處理請求,但在大多數情況下,10000不會佔用過多的內存,而是合理的數量。

這裏的其他優化是,查詢選擇的唯一項目是那些目前由「運算符」來標識的「字符串」。如果某些數據已經被轉換,這可能會加快速度。

如果確實有MongoDB的早期版本,並且您對不在分片羣集上的集合運行此轉換,則您的其他選項是使用db.eval()

照顧實際上請閱讀該鏈接上的內容雖然。這不是一個好主意,你不應該在生產中使用它,而只能作爲一次性轉換的最後手段。代碼以JavaScript的形式提交併在服務器上運行。因此,運行時會發生高級別的鎖定。你一直警告

db.eval(function() { 
    db.play_sessions.find({ "user_id": { "$type": 2 } }).forEach(function(doc) { 
     db.play_sessions.update(
      { "_id": doc._id }, 
      { "$set": { "user_id": NumberInt(doc.user_id) } } 
     ); 
    }); 
}); 

謹慎使用,喜歡的「批量」處理,甚至基本的循環儘可能接近在網絡方面的實際數據庫服務器的機器上。最好在服務器上。

此外,在版本允許的情況下,您仍然認爲eval必要情況下,儘可能使用批量操作方法,因爲這是非常優化的方法。