Невозможно создать индекс в 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}
, он работал нормально.
Чтобы быть ясным, все, что я уверен, сработало здесь, это то, что это позволило мне создать индекс. Вопрос о том, работает ли этот индекс для этого запроса, является отдельным вопросом, на который я еще не ответил.