web-dev-qa-db-fra.com

Quelle est l'importance de l'ordre des indices composés dans la performance de MongoDB?

Nous devons créer un index composé dans le même ordre que celui dans lequel les paramètres sont interrogés. Cet ordre est-il important du point de vue des performances?

Imaginons que nous ayons une collection de tous les humains sur terre avec un index sur sex (99,9% du temps, "mâle" ou "femelle", mais avec une chaîne non irréprochable (non binaire)) et un index sur name.

Si nous souhaitons pouvoir sélectionner toutes les personnes d’un certain sex avec un certain name, par ex. tous les "hommes" sont nommés "John" , vaut-il mieux avoir un indice composé avec sex en premier ou name en premier? Pourquoi pas)?

15
Redsandro

Redsandro,

Vous devez envisager Index Cardinality et Selectivity.


1. Cardinalité d'index

La cardinalité d'index fait référence au nombre de valeurs possibles pour un champ. Le champ sex n'a que deux valeurs possibles. Il a un très faible cardinalité. D'autres champs tels que names, usernames, phone numbers, emails, etc. auront une valeur plus unique pour chaque document de la collection, qui est considérée cardinalité élevée

  • Cardinalité supérieure

    Plus la cardinalité d'un champ est importante, plus un index sera utile, car les index réduisent l'espace de recherche, ce qui en fait un ensemble beaucoup plus petit.

    Si vous avez un index sur sex et que vous recherchez un homme nommé John. Vous ne réduisez l’espace des résultats d’environ 50% que si vous avez indexé en premier par sex. À l'inverse, si vous indexiez avec name, vous réduiriez immédiatement le résultat à une fraction infime d'utilisateurs nommés John, puis vous vous référiez à ces documents pour vérifier le sexe. 

  • Règle du pouce

    Essayez de créer des index sur les clés high-cardinality ou mettez les clés high-cardinality en premier dans l’index composé. Vous pouvez en savoir plus à ce sujet dans la section sur les index composés du livre:

    MONGODB LE GUIDE DÉFINITIF


2. Sélectivité

De plus, vous souhaitez utiliser les index sélectivement _ et écrire des requêtes qui limitent le nombre de documents possibles avec le champ indexé. Pour rester simple, considérez la collection suivante. Si votre index est {name:1}, Si vous exécutez la requête { name: "John", sex: "male"}. Vous devrez numériser le document 1. Parce que vous avez autorisé MongoDB à être sélectif.

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

Considérez la collection suivante. Si votre index est {sex:1}, Si vous exécutez la requête {sex: "male", name: "John"}. Vous devrez numériser des documents 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"}

Imaginez les différences possibles sur un plus grand ensemble de données.


Une petite explication des indices composés

Il est facile de faire la fausse hypothèse sur les indices composés. Selon le Guide des index MongoDB

MongoDB prend en charge les index composés, où une structure d'index unique Contient des références à champs multiples dans les documents d'une collection. .____.] deux champs:

enter image description here

Lorsque vous créez un index composé, 1 Index contiendra plusieurs champs. Donc, si nous indexons une collection par {"sex" : 1, "name" : 1}, l'index ressemblera à peu près à:

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

Si nous indexons une collection par {"name" : 1, "sex" : 1}, l'index ressemblera à peu près à:

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

Avoir {name:1} comme préfixe vous servira beaucoup mieux en utilisant des index composés. Il y a beaucoup plus qui peut être lu sur le sujet, j'espère que cela peut offrir une certaine clarté.

41
Abdullah Rasheed

Je vais dire que j’ai fait une expérience sur ce sujet moi-même et que j’ai constaté qu’il ne semblait pas y avoir de pénalité de performance pour l’utilisation de la clé d’index mal distinguée en premier. (J'utilise mongodb 3.4 avec wiredtiger, qui peut être différent de mmap). J'ai inséré 250 millions de documents dans une nouvelle collection appelée items. Chaque doc ressemblait à ceci:

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

"field1" était toujours égal à "bob". "field2" était égal à i, donc c'était tout à fait unique. J'ai d'abord fait une recherche sur field2 et il a fallu plus d'une minute pour numériser 250 millions de documents. Ensuite, j'ai créé un index comme suit:

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

Bien sûr, champ1 est "bob" sur chaque document. L'index doit donc rechercher un certain nombre d'éléments avant de trouver le document souhaité. Cependant, ce n'est pas le résultat que j'ai obtenu.

J'ai fait une autre recherche sur la collection après la création de l'index. Cette fois, j'ai eu des résultats que j'ai énumérés ci-dessous. Vous verrez que "totalKeysExamined" est égal à 1 à chaque fois. Alors peut-être qu'avec un tigre câblé ou quelque chose comme ça, ils ont compris comment mieux faire cela. J'ai lu le wiredtiger qui compresse en fait les préfixes d'index, cela peut donc avoir quelque chose à voir avec cela.

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

Ensuite, j'ai créé un index sur field3 (qui a la même valeur que le champ 2). Puis j'ai cherché:

db.items.find ({field3: "250888000"});

Il a fallu les mêmes 4ms que celui avec l'indice composé. Je l'ai répété plusieurs fois avec des valeurs différentes pour field2 et field3 et j'ai obtenu des différences insignifiantes à chaque fois. Ceci suggère qu'avec wiredtiger, il n'y a aucune pénalité de performance pour avoir une différenciation médiocre sur le premier champ d'un index.

1
user3413723