集合事件包含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发现扫描所有项目比使用索引更有效吗?
答案 0 :(得分:11)
MongoDB的查询优化器以特殊方式工作。它不是计算某个查询计划的成本,而是启动所有可用的计划。无论哪个首先返回都被认为是最佳的,并将在未来使用。
应用程序增长,数据增长和变化,最佳计划可能在某些时候变得不是最优的。因此,mongo每隔一段时间重复一次查询选择过程。
看来,在这个具体案例中,基本扫描效率最高。
答案 1 :(得分:2)
使用$ hint强制使用索引“events.eventDate”,nscannedObjects不仅仅是索引的w / o。
使用索引时的伪代码:
for(all entries in index matching the criteria) {
get user object and scan to see if the eventId criteria is met
}
符合条件的索引中的所有条目 - >每个事件都是索引中的一个条目。因此索引中的条目数将超过用户数。假设有4个用户对象和总共7个符合条件的事件,用户对象被扫描7次(循环执行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" : {
}