Mongo DB - 索引会使查询变慢

3

我有大约一百万个文档在一个集合中(随机生成的)。

示例文档:

{
    "loc": {
        "lat": 39.828475,
        "lon": 116.273542
    },
    "phone": "",
    "keywords": [
        "big",
        "small",
        "biggest",
        "smallest"
    ],
    "prices": [
        {
            "category": "uRgpiamOVTEQ",
            "list": [
                {
                    "price": 29,
                    "name": "ehLYoPpntlil"
                }
            ]
        },
        {
            "category": "ozQNmdwpwhXPnabZ",
            "list": [
                {
                    "price": 96,
                    "name": "ITTaLHf"
                },
                {
                    "price": 88,
                    "name": "MXVgJFBgtwLYk"
                }
            ]
        },
        {
            "category": "EDkfKGZSou",
            "list": [
                {
                    "price": 86,
                    "name": "JluoCLnenOqDllaEX"
                },
                {
                    "price": 35,
                    "name": "HbmgMDfxCOk"
                },
                {
                    "price": 164,
                    "name": "BlrUD"
                },
                {
                    "price": 106,
                    "name": "LOUcsMDeaqVm"
                },
                {
                    "price": 14,
                    "name": "rDkwhN"
                }
            ]
        }
    ],
}

无索引搜索

> db.test1.find({"prices.list.price": { $gt: 190 } }).explain()
{
    "cursor" : "BasicCursor",
    "isMultiKey" : false,
    "n" : 541098,
    "nscannedObjects" : 1005584,
    "nscanned" : 1005584,
    "nscannedObjectsAllPlans" : 1005584,
    "nscannedAllPlans" : 1005584,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 8115,
    "nChunkSkips" : 0,
    **"millis" : 13803,**
    "server" : "localhost:27017",
    "filterSet" : false
}

带有索引:
> db.test1.ensureIndex({"prices.list.price":1,"menu.list.name":1})
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}

> db.test1.find({"prices.list.price": { $gt: 190 } }).explain()
{
    "cursor" : "BtreeCursor prices.list.price_1_prices.list.name_1",
    "isMultiKey" : true,
    "n" : 541098,
    "nscannedObjects" : 541098,
    "nscanned" : 868547,
    "nscannedObjectsAllPlans" : 541098,
    "nscannedAllPlans" : 868547,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 16852,
    "nChunkSkips" : 0,
    **"millis" : 66227,**
    "indexBounds" : {
        "menu.list.price" : [
            [
                190,
                Infinity
            ]
        ],
        "menu.list.name" : [
            [
                {
                    "$minElement" : 1
                },
                {
                    "$maxElement" : 1
                }
            ]
        ]
    },
    "server" : "localhost:27017",
    "filterSet" : false
}

您有没有想法为什么建立索引后的搜索比未建立索引的慢?

此外,我将使用:

db.test1.find( { loc : { $near : [39.876045, 32.862245]}}) (需要2d索引)

db.test1.find({ keywords:{$in: [ "small", "another" ] }}) (对于关键字使用索引)

db.test1.find({"prices.list.name":/.s./ }) (不需要建立索引,因为我将使用正则表达式)

2个回答

8
索引允许更快地访问满足查询条件的文档的位置。在您的示例中,查询选择了集合中所有文档的一半。因此,即使索引扫描提供更快的访问来确定哪些文档将匹配查询谓词,但它实际上会创建更多的工作量。在集合扫描中,查询正在扫描所有文档,并检查您查询的字段是否匹配。一半时间它最终会选择该文档。在索引扫描中,查询遍历了一半的所有索引条目,然后直接跳转到满足查询谓词的文档。这是您的情况下执行更多操作。除此之外,在执行操作时,当需要等待将要读取的文档被带入RAM或者存在等待写入的写操作时,操作会产生读互斥。相比于集合扫描,索引扫描显示出两倍的产生读互斥次数。如果您的工作集没有足够的RAM,则添加索引将对现有资源施加更多压力,并使事情变得更慢,而不是更快。尝试使用价格进行相同的查询,与较大的数字进行比较,例如500(或数据集中更具选择性的数字)。如果使用索引的查询仍然较慢,则很可能在系统上看到了大量的页面故障。但是,如果索引有足够的RAM,则带索引的查询将比不带索引的查询快得多。

@asya讲解得非常好。那么,为什么mongo使用索引而不使用另一种不带索引的替代方案呢? - MuriloKunze

0

首先,作为建议,使用elemMatch查询数组时可以更快地获取结果。 http://docs.mongodb.org/manual/reference/operator/query/elemMatch/ 在您的情况下

db.test1.find({"prices.list.price":{ $elemMatch: { $gte: 190 }}  })

第二件事是

为了索引一个包含数组值的字段,MongoDB会为数组中的每个项添加索引项。这些多键索引允许MongoDB使用数组的值从查询中返回文档。如果索引字段包含数组值,则MongoDB会自动确定是否创建多键索引;您不需要显式指定多键类型。

考虑以下多键索引的示例: enter image description here

在addr.zip字段上的多键索引图表。addr字段包含地址文档的数组。地址文档包含zip字段。

多键索引支持其他MongoDB索引支持的所有操作;但是,应用程序可以使用多键索引根据数组值的范围选择文档。多键索引支持同时包含值(例如字符串、数字)和嵌套文档的数组。

来自http://docs.mongodb.org/manual/core/index-multikey/


1
$elemMatch并不能使查询更快,但是当您在单个数组元素内匹配多个字段时,它可以使查询更加“正确”。无论如何,查询变慢的原因不是多键索引,而是查询选择性不高。 - Asya Kamsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接