我在MongoDB中有一个集合(app_logins),它包含具有以下结构的文档:
{
"_id" : "c8535f1bd2404589be419d0123a569de"
"app" : "MyAppName",
"start" : ISODate("2014-02-26T14:00:03.754Z"),
"end" : ISODate("2014-02-26T15:11:45.558Z")
}
由于文档说$或者的查询可以并行执行并且可以使用单独的索引,并且我假设$和的相同,我添加了以下索引:
db.app_logins.ensureIndex({app:1})
db.app_logins.ensureIndex({start:1})
db.app_logins.ensureIndex({end:1})
但是当我这样查询时,会扫描太多文件:
db.app_logins.find(
{
$and:[
{ app : "MyAppName" },
{
$or:[
{
$and:[
{ start : { $gte:new Date(1393425621000) }},
{ start : { $lte:new Date(1393425639875) }}
]
},
{
$and:[
{ end : { $gte:new Date(1393425621000) }},
{ end : { $lte:new Date(1393425639875) }}
]
},
{
$and:[
{ start : { $lte:new Date(1393425639875) }},
{ end : { $gte:new Date(1393425621000) }}
]
}
]
}
]
}
).explain()
{
"cursor" : "BtreeCursor app_1",
"isMultiKey" : true,
"n" : 138,
"nscannedObjects" : 10716598,
"nscanned" : 10716598,
"nscannedObjectsAllPlans" : 10716598,
"nscannedAllPlans" : 10716598,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 30658,
"nChunkSkips" : 0,
"millis" : 38330,
"indexBounds" : {
"app" : [
[
"MyAppName",
"MyAppName"
]
]
},
"server" : "127.0.0.1:27017"
}
我知道这可能是因为10716598匹配'app'字段,但另一个查询可以返回一个小得多的子集。
有什么办法可以优化吗?我想到了聚合框架,但我认为可能有更好的方法来优化它,可能使用索引。
修改
如果我在app-start-end上添加一个索引,就像Josh建议的那样,我的结果会更好。我不确定我是否可以通过这种方式进一步优化,但结果要好得多:
{
"cursor" : "BtreeCursor app_1_start_1_end_1",
"isMultiKey" : false,
"n" : 138,
"nscannedObjects" : 138,
"nscanned" : 8279154,
"nscannedObjectsAllPlans" : 138,
"nscannedAllPlans" : 8279154,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 2934,
"nChunkSkips" : 0,
"millis" : 13539,
"indexBounds" : {
"app" : [
[
"MyAppName",
"MyAppName"
]
],
"start" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
],
"end" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
]
},
"server" : "127.0.0.1:27017"
}
答案 0 :(得分:1)
您可以使用compound index来进一步提高效果。
尝试使用.ensureIndex({app:1, start:1, end:1})
这将允许mongo使用索引在app
上匹配,然后在app
上匹配的文档中,它也将使用索引在start
上匹配。同样,对于在start
上匹配的文档中app
匹配的文档,它将使用索引在end
上匹配。
答案 1 :(得分:1)
我怀疑$并且是并行执行的。我也没有看到任何文件建议。它在逻辑上没有意义,因为$并且需要两者都存在。反对$或者,只需要存在1个。 您的示例仅使用“start”&没有“app”的“结束”。我会在复杂的索引中删除“app”,这应该会减小索引大小。如果您的数据库变得太大,它将减少RAM交换的机会。 如果搜索“app”与“start”分开并且& “结束”,然后只在“app”上有一个单独的简单索引,再加上“start”&的复杂索引。 “结束”将更有效率。