web-dev-qa-db-fra.com

Impossible de créer un index dans mongodb, "clé trop grande pour indexer"

Je crée un index dans mongodb ayant 10 millions d'enregistrements mais suite à une erreur

db.logcollection.ensureIndex({"Module":1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 3,
        "ok" : 0,
        "errmsg" : "Btree::insert: key too large to index, failing play.logcollection.$Module_1 1100 { : \"RezGainUISystem.Net.WebException: The request was aborted: The request was canceled.\r\n   at System.Net.ConnectStream.InternalWrite(Boolean async, Byte...\" }",
        "code" : 17282
}

S'il vous plaît, aidez-moi à créer un index dans mongodb,

27
Sandeep.maurya

MongoDB ne créera pas d'index sur une collection si l'entrée d'index d'un document existant dépasse la limite de clé d'index (1024 octets). Vous pouvez cependant créer un index haché ou index de texte à la place:

db.logcollection.createIndex({"Module":"hashed"})

ou

db.logcollection.createIndex({"Module":"text"})
37
anhlc

Vous pouvez désactiver ce comportement en lançant l'instance mongod avec la commande suivante:

mongod --setParameter failIndexKeyTooLong=false

ou en exécutant la commande suivante à partir de mongoShell

db.getSiblingDB('admin').runCommand( { setParameter: 1, failIndexKeyTooLong: false } )

Si vous vous êtes assuré que votre champ dépassera très rarement la limite, alors une façon de résoudre ce problème est de diviser votre champ (ce qui provoque l'index hors de la limite) en parties par longueur d'octet <1 Ko, par ex. pour le champ val je le diviserais en Tuple de champs val_1, val_2 et ainsi de suite. Mongo stocke le texte en tant que valeurs valides utf-8. Cela signifie que vous avez besoin d'une fonction qui peut diviser correctement les chaînes utf-8.

   def split_utf8(s, n):
    """
    (ord(s[k]) & 0xc0) == 0x80 - checks whether it is continuation byte (actual part of the string) or jsut header indicates how many bytes there are in multi-byte sequence

    An interesting aside by the way. You can classify bytes in a UTF-8 stream as follows:

    With the high bit set to 0, it's a single byte value.
    With the two high bits set to 10, it's a continuation byte.
    Otherwise, it's the first byte of a multi-byte sequence and the number of leading 1 bits indicates how many bytes there are in total for this sequence (110... means two bytes, 1110... means three bytes, etc).
    """
    s = s.encode('utf-8')
    while len(s) > n:
        k = n
        while (ord(s[k]) & 0xc0) == 0x80:
            k -= 1
        yield s[:k]
        s = s[k:]
    yield s

Ensuite, vous pouvez définir votre indice composé:

db.coll.ensureIndex({val_1: 1, val_2: 1, ...}, {background: true})

ou plusieurs index pour chaque val_i:

db.coll.ensureIndex({val_1: 1}, {background: true})
db.coll.ensureIndex({val_1: 2}, {background: true})
...
db.coll.ensureIndex({val_1: i}, {background: true})

Important: Si vous envisagez d'utiliser votre champ dans un index composé, faites attention au deuxième argument de la fonction split_utf8. À chaque document, vous devez supprimer la somme des octets de chaque valeur de champ qui composent votre clé d'index, par exemple pour l'index (a: 1, b: 1, val: 1) 1024 - sizeof(value(a)) - sizeof(value(b))

Dans tous les autres cas, utilisez soit les index hachage ou texte .

19
Rustem K

Comme différentes personnes l'ont souligné dans les réponses, l'erreur key too large to index signifie que vous essayez de créer un index sur un ou plusieurs champs d'une longueur supérieure à 1024 octets.

En termes ASCII termes, 1024 octets se traduisent généralement par une longueur d'environ 1024 caractères.

Il n'y a aucune solution pour cela, car il s'agit d'une limite intrinsèque définie par MongoDB comme mentionné dans page Limites et seuils MongoDB :

La taille totale d'une entrée d'index, qui peut inclure une surcharge structurelle en fonction du type BSON, doit être inférieure à 1024 octets.

Activer l'erreur failIndexKeyTooLong n'est pas une solution, comme mentionné dans la page de manuel des paramètres du serveur :

... ces opérations réussiraient à insérer ou à modifier un document, mais l'index ou les index n'incluraient pas de références au document.

Ce que signifie cette phrase, c'est que le document incriminé ne sera pas inclus dans l'index, et peut être absent des résultats de la requête .

Par exemple:

> db.test.insert({_id: 0, a: "abc"})

> db.test.insert({_id: 1, a: "def"})

> db.test.insert({_id: 2, a: <string more than 1024 characters long>})

> db.adminCommand( { setParameter: 1, failIndexKeyTooLong: false } )

> db.test.find()
{"_id": 0, "a": "abc"}
{"_id": 1, "a": "def"}
{"_id": 2, "a": <string more than 1024 characters long>}
Fetched 3 record(s) in 2ms

> db.test.find({a: {$ne: "abc"}})
{"_id": 1, "a": "def"}
Fetched 1 record(s) in 1ms

En forçant MongoDB à ignorer l'erreur failIndexKeyTooLong, la dernière requête ne contient pas le document incriminé (c'est-à-dire le document avec _id: 2 est manquant dans le résultat), donc la requête a abouti au mauvais ensemble de résultats.

8
kevinadi

Lors de l'exécution dans " limite de clé d'index ", la solution dépend des besoins de votre schéma. Dans des cas extrêmement rares, la correspondance des clés sur une valeur> 1024 octets est une exigence de conception. En fait, presque toutes les bases de données imposent une restriction de limite de clé d'index, mais généralement quelque peu configurable dans les bases de données relationnelles héritées (Oracle/MySQL/PostgreSQL), de sorte que vous pouvez facilement vous tirer une balle dans le pied.

Pour une recherche rapide, un index "texte" est conçu pour optimiser la recherche et la mise en correspondance de motifs sur de longs champs de texte, et est bien adapté au cas d'utilisation. Cependant, plus généralement, une contrainte d'unicité sur les valeurs de texte longues est une exigence. Et les index "texte" ne se comportent pas comme le fait une valeur scalaire unique avec l'indicateur unique défini { unique: true } (plus comme un tableau de toutes les chaînes de texte du champ).

S'inspirant du GridFS de MongoDb, les vérifications d'unicité peuvent facilement être implémentées en ajoutant un champ "md5" au document et en créant un index scalaire unique à ce sujet. Un peu comme un index haché unique personnalisé. Cela permet une longueur de champ de texte pratiquement illimitée (~ 16 Mo), indexée pour la recherche et unique dans la collection.

const md5 = require('md5');
const mongoose = require('mongoose');

let Schema = new mongoose.Schema({
  text: {
    type: String,
    required: true,
    trim: true,
    set: function(v) {
        this.md5 = md5(v);
        return v;
    }
  },
  md5: {
    type: String,
    required: true,
    trim: true
  }
});

Schema.index({ md5: 1 }, { unique: true });
Schema.index({ text: "text" }, { background: true });
3
JoelABair