在mongoDB中查询改变string需要很长的时间

我在mongoDB中的整个数据集(大约300万个文档)上运行以下查询,将string的用户标识更改为int。 这个查询似乎并不完整:

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); }; }); 

我知道我写在第一个查询中的数据库,但为什么第一个查询似乎永远不会返回,而第二个这样做相对较快? 第一个查询中的代码是否有问题?

三百万份文件是相当多的文件,所以整个操作将需要一段时间。 但是这里要考虑的主要问题是,你要求将数据“发送”到数据库,并“接收”一个确认的写入响应(因为这是发生了什么)三百万次。 单单是在操作之间等待更多,而不是简单地迭代游标。

这里的另一个原因是,你很可能正在运行MongoDB 2.6或更高版本。 早期版本和版本之间有一个核心区别,就是这个代码是如何在shell中处理的。 其核心是批量操作API ,其中包含所有与shell交互的所有shell帮助程序实际使用的方法。

在以前的版本中,在这样的“循环”操作中,对于每次迭代,在这种情况下“写入关注”确认没有完成。 现在完成的方式(因为帮助者实际上使用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是不会占用太多内存的合理数量。

这里的另一个优化是查询select的唯一项目是那些目前由$type运算符标识的“string”。 如果一些数据已经被转换,这可能会加快速度。

如果确实有早期版本的MongoDB,并且您正在对不在分片群集上的集合运行此转换,那么您的其他select是使用db.eval()

实际上仔细阅读该链接上的内容。 这不是一个好主意,你不应该在生产中使用它,而只能作为一次性转换的最后手段。 代码以JavaScript的forms提交,实际上在服务器上运行。 因此,高级别的locking可以在运行时发生。 你已经被警告

 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 ) } } ); }); }); 

请谨慎使用,并且要尽可能靠近networking条件,尽可能靠近机器上的“批处理”处理甚至基本循环,以便与实际的数据库服务器保持一致。 最好在服务器上。

在版本允许的情况下,您仍然认为必要的情况下,尽量使用批量操作方法,因为这是非常优化的方法。