$ exists存在的查询比扫描整个集合更快吗?

时间:2014-08-28 13:21:48

标签: mongodb

我有一个拥有1000万到1500万条目的mongodb数据库。对于它们中的每一个,我必须更新最初不存在的字段。假设应用程序因服务器意外关闭而崩溃,如何更新剩余的条目?

我应该使用field: {$exists: false}并更新它们,还是应该浏览整个集合并检查每个文档是否包含该字段,如果有,请执行更新?我对此的看法是,由于您无法将索引与字段的存在关联起来,因此$ exists基本上是相同的。哪一个更快,为什么?

请注意,此字段的值将取决于文档的其他字段,因此我无法进行多次更新。

解决方案:正如@DhruvPathak和@Sammaye建议的那样,索引与数据相关联,而不是字段本身(因此您无法将索引链接到字段的存在) ,$ exists可以利用字段存在的文档中的索引,这大大提高了速度。

附加:虽然它有点像副作用,但我现在知道应用程序崩溃的原因。服务器超时了游标,因为它的使用时间太长(给定集合的大小)。如here所解释的那样使用batch_size可以避免这种情况。

3 个答案:

答案 0 :(得分:2)

  

我对此的看法是,因为您无法将索引与字段的存在相关联

根据MongoDB 2.6,它现在可以:

> use f
switched to db f
> db.t.insert({a:1})
WriteResult({ "nInserted" : 1 })
> db.t.ensureIndex({b:1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}
> db.t.find({b:{$exists:false}})
{ "_id" : ObjectId("53e88a7dde0848171584d296"), "a" : 1 }
> db.t.find({b:{$exists:false}}).explain()
{
        "cursor" : "BtreeCursor b_1",
        "isMultiKey" : false,
        "n" : 1,
        "nscannedObjects" : 1,
        "nscanned" : 1,
        "nscannedObjectsAllPlans" : 1,
        "nscannedAllPlans" : 1,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 0,
        "indexBounds" : {
                "b" : [
                        [
                                null,
                                null
                        ]
                ]
        },
        "server" : "ubuntu:27017",
        "filterSet" : false
}
>

从我可以发现它似乎是从v2.0开始添加的内容:https://stackoverflow.com/a/7503114/383478不幸的是,该答案中的文档链接已经死亡。

因此,除非您拥有v2.0之前的版本,否则查询显然会更快。

答案 1 :(得分:1)

您的查询附加explain()将显示$exists的光标类型为Basic:这意味着它将扫描集合中的所有文档。因此,您可以遍历整个集合并检查每个文档是否包含该字段,如果有,则执行更新。 $exists的性能类似于完整扫描集合。

答案 2 :(得分:1)

是的,你是对的,在这两种情况下,mongodb将迭代整个集合。 另一个实现此目的的好方法是在此字段上创建索引, 使用$exists运算符选择目标值,然后删除索引。 确保创建的索引不是稀疏的,因为它在$ exists情况下没有帮助。 http://docs.mongodb.org/manual/core/index-sparse/