Невозможно создать индекс в mongodb, "слишком большой для индекса"

Я создаю индекс в mongodb с 10 миллионами записей, но после ошибки

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
}

Пожалуйста, помогите мне создать createindex в mongodb,

Ответы

Ответ 1

MongoDB не будет создавать индекс для коллекции, если запись индекса для существующего документа превышает ограничение ключа индекса (1024 байта). Однако вы можете вместо этого создать хешированный индекс или текстовый индекс:

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

или же

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

Ответ 2

Вы можете отключить это поведение, запустив экземпляр mongod с помощью следующей команды:

mongod --setParameter failIndexKeyTooLong=false

или выполнив следующую команду из mongoShell

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

Если вы обеспечили, чтобы ваше поле превышало предел очень редко, одним из способов решения этой проблемы является разделение поля (которое приводит к превышению индекса) на части по длине байта < 1KB, например. для поля val я разделил бы его на кортежи полей val_1, val_2 и так далее. Mongo хранит текст как допустимые значения utf-8. Это означает, что вам нужна функция, которая может правильно разделять строки 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 a single byte value.
    With the two high bits set to 10, it a continuation byte.
    Otherwise, it 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

Затем вы можете определить свой составной индекс:

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

или несколько индексов на каждый 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})

Важно: если вы рассматриваете использование своего поля в составном индексе, будьте осторожны со вторым аргументом для функции split_utf8. В каждом документе вам нужно удалить сумму байтов каждого значения поля, которое содержит ваш индексный ключ, например. для индекса (a: 1, b: 1, val: 1) 1024 - sizeof(value(a)) - sizeof(value(b))

В любых других случаях используйте hash или text.

Ответ 3

Как указали разные люди в ответах, key too large to index ошибки означает, что вы пытаетесь создать индекс для поля или полей, длина которых превышает 1024 байта.

В терминах ASCII длина 1024 байта обычно составляет около 1024 символов.

Для этого нет решения, так как это встроенный лимит, установленный MongoDB, как указано на странице MongoDB Limits and Thresholds:

Общий размер элемента индекса, который может включать структурные издержки в зависимости от типа BSON, должен быть менее 1024 байтов.

Включение ошибки failIndexKeyTooLong не является решением, как упомянуто на странице справочника параметров сервера:

... эти операции могли бы успешно вставить или изменить документ, но индекс или индексы не будут включать ссылки на документ.

Это предложение означает, что оскорбительный документ не будет включен в индекс и может отсутствовать в результатах запроса.

Например:

> 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

failIndexKeyTooLong MongoDB игнорировать ошибку failIndexKeyTooLong, последний запрос не содержит нарушающий документ (т. failIndexKeyTooLong Документ с _id: 2 отсутствует в результате), таким образом, запрос привел к неверному набору результатов.

Ответ 4

При работе с " пределом ключа индекса " решение зависит от потребностей вашей схемы. В крайне редких случаях соответствие ключа со значением> 1024 байта является требованием к проектированию. Фактически, почти все базы данных налагают ограничение на ограничение ключа индекса, но, как правило, в некоторой степени настраиваются в унаследованных реляционных БД (Oracle/MySQL/PostgreSQL), так что вы можете легко выстрелить себе в ногу.

Для быстрого поиска "текстовый" индекс предназначен для оптимизации поиска и сопоставления с образцом в длинных текстовых полях и хорошо подходит для варианта использования. Тем не менее, чаще всего требуется ограничение уникальности длинных текстовых значений. И "текстовые" индексы не ведут себя так же, как и уникальное скалярное значение с уникальным флагом set { unique: true } (больше похоже на массив всех текстовых строк в поле).

Черпая вдохновение из MongoDb GridFS, можно легко осуществить проверку уникальности, добавив в документ поле "md5" и создав для него уникальный скалярный индекс. Вроде как пользовательский уникальный хешированный индекс. Это позволяет использовать практически неограниченную длину текстового поля (~ 16 МБ), которая индексируется для поиска и уникальна для всей коллекции.

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

Ответ 5

В моем случае я пытался индексировать большой массив вложенных документов, и когда я пошел и посмотрел на свой запрос, запрос был на самом деле для подпрепарата из подпрепарата, поэтому я изменил индекс, чтобы сосредоточиться на указанном подпрепарате, и он работал нормально.

В моем случае goals был большим массивом вложенных документов, ошибочный индекс "слишком большой ключ" выглядел как {"goals": 1, "emailsDisabled": 1, "priorityEmailsDisabled": 1}, а запрос выглядел так:

emailsDisabled: {$ne: true},
priorityEmailsDisabled: {$ne: true},
goals: {
  $elemMatch: {
    "topPriority.ymd": ymd,
  }
}

и как только я изменил индекс на {"goals.topPriority.ymd": 1, "emailsDisabled": 1, "priorityEmailsDisabled": 1}, он работал нормально.

Чтобы быть ясным, все, что я уверен, сработало здесь, это то, что это позволило мне создать индекс. Вопрос о том, работает ли этот индекс для этого запроса, является отдельным вопросом, на который я еще не ответил.