8

集合事件有 userId 和一个事件数组——数组中的每个元素都是一个嵌入的文档。例子:

{
    "_id" : ObjectId("4f8f48cf5f0d23945a4068ca"),
    "events" : [
            {
                    "eventType" : "profile-updated",
                    "eventId" : "247266",
                    "eventDate" : ISODate("1938-04-27T23:05:51.451Z"),
            },
           {
                   "eventType" : "login",
                   "eventId" : "64531",
                   "eventDate" : ISODate("1948-05-15T23:11:37.413Z"),
           }
    ],
    "userId" : "junit-19568842",

}

使用如下查询来查找过去 30 天内生成的事件:

db.events.find( { events : { $elemMatch: { "eventId" : 201, 
"eventDate" : {$gt : new Date(1231657163876) } } } }  ).explain()

查询计划显示,当测试数据包含较少的事件(大约 20 个)时,使用“events.eventDate”上的索引:

{
    "cursor" : "BtreeCursor events.eventDate_1",
    "nscanned" : 0,
    "nscannedObjects" : 0,
    "n" : 0,
    "millis" : 0,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : true,
    "indexOnly" : false,
    "indexBounds" : {
            "events.eventDate" : [
                    [
                            ISODate("2009-01-11T06:59:23.876Z"),
                            ISODate("292278995-01--2147483647T07:12:56.808Z")
                    ]
            ]
    }

}

但是,当有大量事件(大约 500 个)时,不使用索引:

{
    "cursor" : "BasicCursor",
    "nscanned" : 4,
    "nscannedObjects" : 4,
    "n" : 0,
    "millis" : 0,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : false,
    "indexOnly" : false,
    "indexBounds" : {

    }

}

为什么在有很多事件时不使用索引?可能是当有大量事件时,MongoDB 发现只扫描所有项目比使用索引更有效?

4

2 回答 2

13

MongoDB 的查询优化器以一种特殊的方式工作。它不计算某些查询计划的成本,而是启动所有可用计划。无论哪个先返回,都被认为是最优的,并将在未来使用。

应用程序增长,数据增长和变化,最佳计划可能在某些时候变得不是最佳的。因此,mongo 每隔一段时间就会重复该查询选择过程。

在这种具体情况下,基本扫描似乎是最有效的。

链接:http ://www.mongodb.org/display/DOCS/Query+Optimizer

于 2012-04-18T23:50:22.570 回答
2

使用 $hint 强制使用索引“events.eventDate”,nscannedObjects 不仅仅是没有索引。

使用索引时的伪代码:

for(all entries in index matching the criteria) {
  get user object and scan to see if the eventId criteria is met
}

索引中与条件匹配的所有条目 --> 每个事件都是索引中的一个条目。所以索引中的条目数会多于用户数。假设有 4 个用户对象和总共 7 个符合条件的事件,用户对象被扫描 7 次(for 循环执行 7 次)。未扫描索引时,所有 4 个用户对象仅检查一次。所以使用索引,用户对象被扫描的次数比不使用索引的时候要多。这种理解对吗?

db.events.find( { events : { $elemMatch: { "eventId" : 201, 
"eventDate" : {$gt : new Date(1231657163876) } } } }  )
._addSpecial("$hint",{"events.eventDate":1}).explain()

{
    "cursor" : "BasicCursor",
    "nscanned" : 7,
    "nscannedObjects" : 7,
    "n" : 0,
    "millis" : 0,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : false,
    "indexOnly" : false,
    "indexBounds" : {

}
于 2012-04-19T20:29:13.607 回答