为啥 MongoDB 在这里使用 scanAndOrder?

Posted

技术标签:

【中文标题】为啥 MongoDB 在这里使用 scanAndOrder?【英文标题】:Why is MongoDB using scanAndOrder here?为什么 MongoDB 在这里使用 scanAndOrder? 【发布时间】:2014-02-26 19:37:40 【问题描述】:

我正在 MongoDB 中保存游戏文档。除其他内容外,这些文件还包含玩家的姓名 (name)、游戏结束的时间 (endMS) 和游戏的类型 (type)。类型可以有五分之一的不同值。

我需要搜索按游戏结束时间排序的玩家完成的所有游戏,以及按游戏结束时间排序的特定游戏类型的玩家完成的所有游戏。

这两个查询的示例是

db.games.find(name:"Stefan",endMS:$gt:0).sort(endMS:-1)

db.games.find(name:"Stefan",type:"bli",endMS:$gt:0).sort(endMS:-1)

你可以使用索引

db.games.ensureIndex(name:1,endMS:-1)

db.games.ensureIndex(name:1,type:1,endMS:-1)

为了快速访问。

现在我尝试只使用一个索引:

db.games.ensureIndex(name:1,endMS:-1,type:1)

第一个查询或课程仍然运行良好。第二个查询的想法是,Mongo 在扫描索引时可能需要跳过一些条目,但只需要访问查询最终返回的文档,因为“类型”已经可以在索引中检查。这应该足以满足我的需要。

但是使用 explain() MongoDB 告诉我在像这样查询数据库时需要“scanAndOrder”。

db.games.find(name:"Stefan",type:"bli",endMS:$gt:0).sort(endMS:-1).explain()


    "cursor" : "BtreeCursor name_1_endMS_-1_type_1",
    "isMultiKey" : false,
    "n" : 1,
    "nscannedObjects" : 1,
    "nscanned" : 22,
    "nscannedObjectsAllPlans" : 4,
    "nscannedAllPlans" : 25,
    "scanAndOrder" : true,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    "indexBounds" : 
        "name" : [
            [
                "Stefan",
                "Stefan"
            ]
        ],
        "endMS" : [
            [
                Infinity,
                0
            ]
        ],
        "type" : [
            [
                "bli",
                "bli"
            ]
        ]
    ,
    "server" : "localhost:27017",
    "filterSet" : false

nscannedObjects 和 nscanned 如上所述,但我想知道为什么 Mongo 说 scanAndOrder:true。

根据文档: “scanAndOrder 是一个布尔值,当查询不能使用索引中文档的顺序返回排序结果时为真:MongoDB 必须在从游标接收文档后对文档进行排序。”

据我了解,文档应该在索引中排序,只需要跳过一些不影响顺序的文档。

那么为什么 MongoDB 在这里使用 scanAndOrder 呢?

【问题讨论】:

您使用的是哪个版本的 MongoDB?奇怪的是,您有一个真正的“scanAndOrder”和一个错误的“indexOnly”。我会说您的查询已被索引覆盖... 我使用的是 MongoDB 2.6.0-rc0。搜索不能是 indexOnly,因为它返回整个文档。它只是“indexOnly”,因为它不需要扫描文档来确定它是否与查询匹配。 奇怪,我已将 _id:0,name:1 作为投影参数添加到查询中,这也不会使查询 indexOnly。它可能与“type”实际上是“tc.type”有关,因此它取自嵌入式文档(我只是想让这里的问题更清楚)。我记得当索引的一部分来自嵌入文档时,MongoDB 的聚合框架中的coveredIndexes 存在问题。但这是否也会影响 scanAndOrder? 我做了更多的测试,似乎从嵌入文档中获取的索引条目在这里不是问题。 我会用 2.4 版本测试它。如果这是一个错误,你应该check this 【参考方案1】:

这似乎是 MongoDB 2.6.0-rc0 中的一个错误。在 MongoDB 2.4.9 中一切正常。

【讨论】:

【参考方案2】:

Stefan 在这里报告了这个问题:http://jira.mongodb.org/browse/SERVER-12935 并且 scanAndOrder 问题在 2.6.0-rc0 之后得到解决,但解释输出存在一些挥之不去的问题。

【讨论】:

【参考方案3】:

http://docs.mongodb.org/manual/reference/glossary/

自然顺序: 数据库在磁盘上存储文档的顺序。通常,磁盘上文档的顺序反映了插入顺序,除非文档在内部移动,因为更新操作会增加其大小。在有上限的集合中,插入顺序和自然顺序是相同的,因为文档不会在内部移动。对于不带参数的 find() 查询,MongoDB 以正向自然顺序返回文档。对于使用 $natural:-1 参数排序的 find() 查询,MongoDB 以逆自然顺序返回文档。见 $natural。

您对插入顺序 == 索引顺序的假设是错误的。

【讨论】:

这不是我的假设。我假设文档是按 endMS 排序的,因为 endMS 是复合索引的第二部分。 好了,你回答了你的问题。这是一个错误:)。如果我希望某个顺序只是为了确定,我总是使用排序,如果性能达不到标准,我会优化其他领域。

以上是关于为啥 MongoDB 在这里使用 scanAndOrder?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我应该在 MongoDB 中使用 NodeJS 在会话案例中搜索用户?

为啥 MongoDB skip() 不使用索引?

为啥 MongoDB 将已删除的文件保留在磁盘上?

spring mongodb updatemulti为啥只更新了一条

为啥以及何时需要在 MongoDB 中重建索引?

为啥在 mongodb 中没有更新 system.profile 集合?