复合索引的顺序在MongoDB中的表现如何?

时间:2015-11-05 13:04:48

标签: mongodb indexing compound-index

我们需要以与查询参数相同的顺序创建复合索引。这个订单在性能方面是否重要?

想象一下,我们拥有地球上所有人类的集合,其中sex的索引(99.9%的时间是“男性”或“女性”,但字符串(不是二元))和{{{ 1}}。

如果我们希望能够选择具有特定name的特定sex的所有人,例如所有名为“John”的“男性”,最好是首先使用name或首先sex的复合索引?为什么(不是)?

3 个答案:

答案 0 :(得分:51)

Redsandro,

您必须考虑 Index Cardinality Selectivity

1。索引基数

索引基数是指字段有多少可能的值。字段sex只有两个可能的值。它具有非常低基数。其他字段(例如names, usernames, phone numbers, emails等)对于集合中的每个文档都具有更独特的值,这被视为高基数

  • 更高的基数

    字段的基数越大,索引就越有用,因为索引会缩小搜索空间,使其变得更小。

    如果您在sex上有索引,并且您正在寻找名为John的男士。如果您首先将sex编入索引,则只会将结果空间缩小约50%。相反,如果您使用name建立索引,则会立即将结果集缩小到名为John的用户的一小部分,然后您将引用这些文档来检查性别。

  • 经验法则

    尝试在high-cardinality键上创建索引或首先将high-cardinality键放在复合索引中。您可以在本书的复合索引部分中阅读更多相关内容:

    <强> MongoDB The Definitive Guide

2。选择性

此外,您希望使用索引selectively并编写限制索引字段的可能文档数的查询。为简单起见,请考虑以下集合。如果您的索引为{name:1},则运行查询{ name: "John", sex: "male"}。您必须扫描1文档。因为你允许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"}

考虑以下集合。如果您的索引为{sex:1},则运行查询{sex: "male", name: "John"}。您必须扫描4个文档。

{_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"}

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

复合指数的一点解释

很容易对复合索引做出错误的假设。根据{{​​3}}。

  

MongoDB支持复合索引,支持单个索引结构   在集合的文档中包含对多个字段的引用。   下图说明了复合索引的示例   两个领域:

     

MongoDB docs on Compound Indexes

创建复合索引时, 1索引将包含多个字段。因此,如果我们通过{"sex" : 1, "name" : 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

如果我们按{"name" : 1, "sex" : 1}索引集合,索引将大致如下:

["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

{name:1}作为enter image description here可以更好地使用复合索引。关于这个主题还有更多内容可以阅读,我希望这可以提供一些清晰度。

答案 1 :(得分:1)

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

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

"field1"始终等于"bob""field2"等于i,因此它完全是唯一的。首先,我对field2进行了搜索,扫描了2.5亿个文档花了一分多钟。然后我创建了一个像这样的索引:

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

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

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

db.items.find({field1:"bob",field2:"250888000"}).explain("executionStats")

{
    "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
        }
    }

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

db.items.find({字段3: “250888000”});

与复合指数相同的4ms。我用field2和field3的不同值重复了这么多次,并且每次都有微不足道的差异。这表明,对于wiretiger,在索引的第一个字段上具有差的差异没有性能损失。

答案 2 :(得分:1)

<块引用>

请注意,多个相等谓词不必按选择性从高到低的顺序排列。过去已经提供了该指南,但是由于 B 树索引的性质以及在叶页中 B 树如何存储所有字段值的组合,它是错误的。因此,无论键顺序如何,组合的数量都完全相同。

https://www.alexbevi.com/blog/2020/05/16/optimizing-mongodb-compound-indexes-the-equality-sort-range-esr-rule/

这篇博客文章不同意接受的答案。另一个答案中的基准也表明这无关紧要。那篇文章的作者是“MongoDB 的高级技术服务工程师”,这对我来说在这个主题上听起来像是一个值得信赖的人,所以我猜这个顺序毕竟不会影响相等字段的性能。我将改为遵循 ESR 规则。

相关问题