提问者:小点点

在MongoDB中,复合索引的顺序对性能有何影响?


我们需要按照与查询参数相同的顺序创建一个复合索引。这个命令对性能有影响吗?

假设我们有一个地球上所有人类的集合,其索引在上(99.9%的情况下是“男性”或“女性”,但不是字符串(不是二进制)),索引在上。

如果我们希望能够用某个选择某个的所有人,例如所有名为“john'”的“男性”,那么是先有一个还是先有一个的复合索引更好呢?为什么(不)?


共2个答案

匿名用户

雷德桑德罗,

您必须考虑

索引基数指的是字段可能有多少个值。字段只有两个可能的值。它具有很低的基数。其他字段(如等)将为集合中的每个文档提供更唯一的值,这被认为是高基数。

>

如果您有一个关于的索引,并且您正在查找名为John的男子。如果首先使用进行索引,则只会将结果空间缩小大约50。相反,如果使用进行索引,则会立即将结果集缩小到一小部分名为John的用户,然后会参考这些文档来检查性别。

null

null

此外,您希望有选择地使用索引,并编写查询,以限制具有索引字段的可能文档的数量。为了保持简单,请考虑下面的集合。如果您的索引是,如果您运行查询。您必须扫描文档。因为您允许MongoDB具有选择性。

{_id:ObjectId(),name:"John",sex:"male"}
{_id:ObjectId(),name:"Rich",sex:"male"}
{_id:ObjectId(),name:"Mose",sex:"male"}
{_id:ObjectId(),name:"Sami",sex:"male"}
{_id:ObjectId(),name:"Cari",sex:"female"}
{_id:ObjectId(),name:"Mary",sex:"female"}

请考虑以下集合。如果您的索引是,如果您运行查询。您将必须扫描文档。

{_id:ObjectId(),name:"John",sex:"male"}
{_id:ObjectId(),name:"Rich",sex:"male"}
{_id:ObjectId(),name:"Mose",sex:"male"}
{_id:ObjectId(),name:"Sami",sex:"male"}
{_id:ObjectId(),name:"Cari",sex:"female"}
{_id:ObjectId(),name:"Mary",sex:"female"}

想象一下在一个更大的数据集上可能存在的差异。

很容易对复合索引做出错误的假设。根据MongoDB关于复合索引的文档。

MongoDB支持复合索引,即单个索引结构保存对集合文档中多个字段的引用。下图说明了两个字段上的复合索引示例:

创建复合索引时,1个索引将包含多个字段。因此,如果我们按对集合进行索引,则索引将大致如下:

["male","Rick"] -> 0x0c965148
["male","John"] -> 0x0c965149
["male","Sean"] -> 0x0cdf7859
["male","Bro"] ->> 0x0cdf7859
...
["female","Kate"] -> 0x0c965134
["female","Katy"] -> 0x0c965126
["female","Naji"] -> 0x0c965183
["female","Joan"] -> 0x0c965191
["female","Sara"] -> 0x0c965103

如果我们按对集合进行索引,则索引大致如下:

["John","male"] -> 0x0c965148
["John","female"] -> 0x0c965149
["John","male"] -> 0x0cdf7859
["Rick","male"] -> 0x0cdf7859
...
["Kate","female"] -> 0x0c965134
["Katy","female"] -> 0x0c965126
["Naji","female"] -> 0x0c965183
["Joan","female"] -> 0x0c965191
["Sara","female"] -> 0x0c965103

使用作为前缀可以更好地帮助您使用复合索引。关于这个话题,还有更多的东西可以阅读,我希望这能提供一些澄清。

匿名用户

我要说的是,我自己做了一个实验,发现首先使用区分性差的索引键似乎没有性能损失。(我正在使用MongoDB3.4和wiredtiger,它可能与mmap不同)。我将2.5亿个文档插入到一个名为的新集合中。每个文档都是这样的:

{
    field1:"bob",
    field2:i + "",
    field3:i + ""

始终等于等于,因此它是完全唯一的。首先,我在field2上进行了搜索,花了一分钟的时间扫描了2.5亿个文档。然后我创建了一个索引,如下所示:

`db.items.createIndex({field1:1,field2:1})`

当然,field1在每个文档上都是“Bob”,因此索引应该在找到所需文档之前搜索许多项。然而,这并不是我得到的结果。

在索引创建完成后,我对集合进行了另一次搜索。这一次,我得到的结果,我列出了下面。您将看到每次都是1。因此,也许通过有线老虎或其他东西,他们已经想出了如何做得更好。我看过wiredtiger实际上压缩索引前缀,所以可能跟它有关系。

{
    "executionSuccess" : true,
    "nReturned" : 1,
    "executionTimeMillis" : 4,
    "totalKeysExamined" : 1,
    "totalDocsExamined" : 1,
    "executionStages" : {
        "stage" : "FETCH",
        "nReturned" : 1,
        "executionTimeMillisEstimate" : 0,
        "works" : 2,
        "advanced" : 1,
        ...
        "docsExamined" : 1,
        "inputStage" : {
            "stage" : "IXSCAN",
            "nReturned" : 1,
            "executionTimeMillisEstimate" : 0,
            ...
            "indexName" : "field1_1_field2_1",
            "isMultiKey" : false,
            ...
            "indexBounds" : {
                "field1" : [
                    "[\"bob\", \"bob\"]"
                ],
                "field2" : [
                    "[\"250888000\", \"250888000\"]"
                ]
            },
            "keysExamined" : 1,
            "seeks" : 1
        }
    }

然后我在上创建了一个索引(它与字段2的值相同)。然后我搜索了一下:

db.items.find(Field3:250888000);

与复合指数法相同,用时4ms。我用field2和field3的不同值重复了多次,每次都得到了不显著的差异。这表明,使用wiredtiger,索引的第一个字段的差分不会导致性能损失。