web-dev-qa-db-fra.com

MongoDB n'utilise qu'un seul index sur une requête simple avec le tri

Donc, le problème est que j'ai une énorme collection et j'essaie d'exécuter une requête qui filtre et trie uniquement sur des champs qui les indexent.

J'ai ces deux index:

db.getCollection('product').createIndex({"_type": 1, "_collectivity": 1, "own_risk": 1});
db.getCollection('product').createIndex({"price": 1});

Et c'est la simple requête que je cours:

db.getCollection('product').find({
    "_type": "healthcare2",
    "_collectivity": null,
    "own_risk": 375
}).sort({price: 1}).explain()

Et voici le winningplan pour cette requête:

{
  "winningPlan": {
    "stage": "FETCH",
    "filter": {
      "$and": [
        {
          "_collectivity": {
            "$eq": null
          }
        },
        {
          "_type": {
            "$eq": "healthcare2"
          }
        },
        {
          "own_risk": {
            "$eq": 375.0000000000000000
          }
        }
      ]
    },
    "inputStage": {
      "stage": "IXSCAN",
      "keyPattern": {
        "price": 1
      },
      "indexName": "price_1",
      "isMultiKey": false,
      "direction": "forward",
      "indexBounds": {
        "price": [
          "[MinKey, MaxKey]"
        ]
      }
    }
  }
}

Cela scanne donc toute la collection ignorant tous les index. Mais si j'essaie d'indiquer:

db.getCollection('product').find({
    "_type": "healthcare2",
    "_collectivity": null,
    "own_risk": 375
}).sort({price: 1}).hint({"_type": 1, "_collectivity": 1, "own_risk": 1})

Mongo me retournera une erreur ...

error: {
    "$err" : "Executor error: Overflow sort stage buffered data usage of 33686190 bytes exceeds internal limit of 33554432 bytes",
    "code" : 17144
}

Mongo n'essaiera pas d'utiliser l'index price après avoir trié tous les documents avec le premier index.

Alors, quel est mon problème? Ou Mongo's? Comment utiliser Mongodb utiliser plus d'un indice dans une requête?

Mon MongoDB est 3.0.6 et courante à Ubuntu à Vagrant.

4
Konstantin Bodnia

MongoDB 2.6+ considérera l'intersection de l'index dans les cas où plusieurs index peuvent améliorer la performance de la requête, mais pour trier l'index doit avoir des clés communes avec le prédicat de la requête. Dans votre scénario, cela signifie qu'un index sur {price:1} ne peut pas être utilisé pour l'intersection de l'index, sauf si price est inclus dans la requête.

Cela scanne donc toute la collection ignorant tous les index.

Ce qui se passe effectivement selon la requête explain() est que l'index price:1 est utilisé pour renvoyer les résultats en ordre trié via une analyse d'index (IXSCAN). Votre indice composé nécessiterait un tri de la mémoire du jeu de résultats, qui peut être supérieur à la taille de la touche tampon de tri en mémoire (32 Mo) Comme vous l'avez découvert en essayant d'indiquer cet index. Si vous pouvez ajouter une limite à votre requête afin que les résultats soient inférieurs à 32 Mo, l'indice composé peut être utilisable mais pas idéal.

Pour votre requête, l'index idéal pour prendre en charge l'ordre de tri souhaité (et un ensemble illimité de résultats) est un index composé comprenant le prix:

db.product.createIndex({"_type": 1, "_collectivity": 1, "own_risk": 1, "price": 1});

Pour plus d'informations, voir:

4
Stennie

Vous pouvez ajouter le "price":1 à la fin de votre indice composé (abandonner l'autre index) et Mongod l'utilisera pour la troisième phase de Trouver et de tri,

db.getCollection('product').createIndex({
  "_type": 1,
  "_collectivity": 1,
  "own_risk": 1 ,
  "price": 1
});

La raison pour laquelle l'optimiseur favorise {price:1} vs L'indice composé est le nombre de résultats que la requête revient.

Le nouvel indice fera le travail mais ne vous attendra pas à voir le gain de performance si le nombre de résultats est proche de la taille de la collection complète.

Son également intéressant à tester l'intersection de l'indice, mais je suppose que le nombre de résultats retournés sera lent.

1
Antonios