【问题标题】:Query performing faster without the index没有索引的查询执行速度更快
【发布时间】:2016-08-22 20:29:29
【问题描述】:

以下是我的数据库中的一个简化版本的文档:

{
    _id : 1,
    main_data : 100,
    sub_docs: [
        {
            _id : a,
            data : 22
        },
        {
            _id: b,
            data : 859
        },
        {
            _id: c,
            data: 151
        },

        ... snip ...

        {
           _id: m,
           data: 721
        },
        {
           _id: n,
           data: 111
        }
    ]
}

所以想象一下,我有一百万个具有不同数据值(例如 0 - 1000)的此类文档。目前我的查询是这样的:

db.myDb.find(
    { sub_docs: { $elemMatch: { data: { $gte: 110, $lt: 160 } } } }
)

另外说上面的查询只会匹配大约 0.001% 的数据(因此总共返回大约 10 个文档)。

我有一个索引集使用:

db.myDb.ensureIndex( sub_docs.data )

对此数据执行定时测试似乎表明在 sub_docs.data 上没有设置任何索引的情况下它更快。

我正在使用 Mongo 3.2.8。

编辑 - 附加信息:

我的定时测试是一个 Perl 脚本,它查询服务器然后拉回相关数据。当我启用索引时,我首先运行了这个测试,但是缓慢的查询时间迫使我做一些挖掘工作。我想看看如果我删除索引,查询时间会有多糟糕,但是它改善了查询的响应时间! 我走得更远了,我绘制了查询响应时间与数据库中文档总数的关系,两个图都显示查询时间线性增加,但查询索引以更快的速度增加. 在进行测试的过程中,我一直在关注服务器内存使用情况(很低),因为我的第一个想法是索引不适合内存。

所以总的来说,我的问题是:为什么对于这个特定的查询,这个查询在没有索引的情况下表现更好? 有没有办法用更好的索引来提高这个查询的速度?

更新

好的,已经有一段时间了,我已将其范围缩小到不限制查询搜索参数两侧的索引。

上面的查询将显示一个索引范围:

[-inf, 160]

而不是 110 到 160。 我可以通过使用 index min 和 max 函数来解决这个问题,如下所示:

db.myDb.find(
    { sub_docs: { $elemMatch: { data: { $gte: 110, $lt: 160 } } } }
).min({'subdocs.data': 110}).max({'subdocs.data': 160})

但是(如果可能的话)我更喜欢不同的方式来做这件事,因为我想使用聚合函数(它似乎不支持最小/最大索引函数)

【问题讨论】:

  • @JohnnyHK 我在上面添加了一些额外的信息,你是绝对正确的,现在有一个实际的问题要回答:-)
  • 谢谢,查看explain 的查询输出并将其添加到您的问题中。

标签: mongodb mongodb-query mongodb-indexes


【解决方案1】:

好的,所以我最终设法对此进行了排序。无论出于何种原因,索引都不会像我预期的那样限制查询。

运行这个:

db.myDb.find({ sub_docs: { $elemMatch: { data: { $gte: 110, $lt: 160 } } } }).explain()

索引的作用片段如下:

                      "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "sub_docs.data" : 1
                                },
                                "indexName" : "sub_docs.data_1",
                                "isMultiKey" : true,
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 1,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "sub_docs.data" : [
                                                "[-inf.0, 160.0)"
                                        ]
                                }
                        }

它不是将索引限制在 110 到 160 之间,而是扫描所有与小于或等于 160 的索引键匹配的文档。 我没有包括它,但另一个被拒绝的计划是索引扫描 110 到 inf+。 您可以使用我在评论中提到的最小/最大限制对这个问题进行排序,但这意味着您不能使用聚合框架,这很糟糕。

所以我找到的解决方案是将我想要索引的所有数据提取到一个数组中:

{
    _id : 1,
    main_data : 100,
    index_values : [
        22,
        859,
        151,

      ...snip...

        721,
        111
    ],
    sub_docs: [
        {
            _id : a,
            data : 22
        },
        {
            _id: b,
            data : 859
        },
        {
            _id: c,
            data: 151
        },

        ... snip ...

        {
           _id: m,
           data: 721
        },
        {
           _id: n,
           data: 111
        }
    ]
}

然后我创建索引:

db.myDb.ensureIndex({index_values : 1})

然后改为查询:

db.myDb.find({ index_values : { $elemMatch: { $gte: 110, $lt: 160 } } }).explain()

产生:

"indexBounds" : {
       "index_values" : [
           "[110.0, 160.0]"
       ]
}

现在要检查的文件少了很多!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-28
    • 1970-01-01
    • 2015-11-28
    • 1970-01-01
    • 2017-01-11
    • 2012-05-24
    • 1970-01-01
    • 2017-10-19
    相关资源
    最近更新 更多