与先前版本相比,为什么2.6 MongoDB shell中的插入速度较慢?

时间:2014-03-28 17:13:28

标签: mongodb

将测试数据插入MongoDB时,我通常只使用for循环来执行大量单个插入操作。使用2.4及更低版本,这非常快(约2秒),例如:

> db.timecheck.drop();
true
> start = new Date(); for(var i = 0; i < 100000; i++){db.timecheck.insert({"_id" : i})}; end = new Date(); print(end - start);
2246

使用2.6尝试相同的操作要慢得多(约37秒):

> db.timecheck.drop();
true
> start = new Date(); for(var i = 0; i < 100000; i++){db.timecheck.insert({"_id" : i})}; end = new Date(); print(end - start);
37169

这要慢得多。那么,为什么新版本存在这样的差异,我该如何解决呢?

1 个答案:

答案 0 :(得分:14)

在2.6之前,交互式shell将运行循环并仅检查循环中最后一个操作的成功(使用getLastError)(更具体地说,在每次回车后调用getLastError,最后一个操作是循环中的最后一个插入)。使用2.6,shell现在将检查循环中每个单独操作的状态。从本质上讲,这意味着2.6的“缓慢”可归因于已确认与未确认的写入性能,而不是实际的性能问题本身。

已确认的写入是default for some time now,因此我认为2.6中的行为更为正确,但对于我们这些习惯于原始行为的人来说有点不方便。

要回到之前的效果级别,答案是使用新的unordered bulk insert API。这是一个定时版本:

> db.timecheck.drop();
true
> var bulk = db.timecheck.initializeUnorderedBulkOp(); start = new Date(); for(var i = 0; i < 100000; i++){bulk.insert({"_id" : i})}; bulk.execute({w:1}); end = new Date(); print(end - start);
2246

现在又恢复到基本相同的性能,仅超过2秒。当然,它有点笨重(原谅双关语),但你确切知道你得到了什么,我认为这是一件好事。当你不寻找时间信息时,这里也有好处。让我们摆脱它并再次运行插入:

> db.timecheck.drop();
true
> var bulk = db.timecheck.initializeUnorderedBulkOp(); for(var i = 0; i < 100000; i++){bulk.insert({"_id" : i})}; bulk.execute({w:1});
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 100000,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})

现在,当我们进行批量插入时,我们得到一个很好的结果文档,而不是仅检查最后的操作(2.4版本中的所有其余部分基本上是发送并忘记)。因为它是无序的批量操作,所以如果它遇到错误并在本文档中报告每个此类错误,它将继续运行。在上面的示例中没有任何内容可以看到,但很容易人为地创建故障情形。让我们预先插入一个我们知道会出现的值,从而导致(默认)唯一_id索引出现重复键错误:

> db.timecheck.drop();
true
> db.timecheck.insert({_id : 500})
WriteResult({ "nInserted" : 1 })
> var bulk = db.timecheck.initializeUnorderedBulkOp(); for(var i = 0; i < 100000; i++){bulk.insert({"_id" : i})}; bulk.execute({w:1});
2014-03-28T16:19:40.923+0000 BulkWriteError({
"writeErrors" : [
{
"index" : 500,
"code" : 11000,
"errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: test.timecheck.$_id_ dup key: { : 500.0 }",
"op" : {
"_id" : 500
}
}
],
"writeConcernErrors" : [ ],
"nInserted" : 99999,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})

现在我们可以看到有多少是成功的,哪一个失败了(以及为什么)。设置可能有点复杂,但总的来说,我认为这是一个改进。

综上所述,以及概述的新首选方式,有一种方法可以将shell强制恢复到传统模式。这是有道理的,因为2.6 shell可能必须连接到旧服务器并与之配合使用。如果您连接到2.4服务器,这将为您处理,但为了强制执行特定连接的问题,您可以运行:

db.getMongo().forceWriteMode("legacy");

完成后,您可以使用以下命令恢复到2.6版本:

db1.getMongo().forceWriteMode("commands");

有关实际用途,请参阅我的crud.js snippet。这在目前有效,但可能会在未来的任何时候被删除,恕不另行通知,并且实际上并不打算广泛使用,因此使用风险自负。