如何使用多个$和$或语句正确索引MongoDB查询

时间:2014-02-26 21:59:36

标签: mongodb

我在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"
}

2 个答案:

答案 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”&的复杂索引。 “结束”将更有效率。