2014-09-03 33 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必要情况下,尽可能使用批量操作方法,因为这是非常优化的方法。

相关问题