Индексы MongoDB

MongoDB

Введение в индекс

1.1 Создайте индекс

Как и большинство реляционных баз данных, MongoDB поддерживает использование индексов для оптимизации запросов, используя структуру данных, подобную B-Tree, для хранения информации о расположении индексов и документов, а также поддерживает префиксные индексы и покрывающие индексы. В текущей последней версии MongoDB 4.0 синтаксис создания индекса выглядит следующим образом:

db.collection.createIndex( <key and index type specification>, <options> )
  • <key and index type specification>: используется для указания таких атрибутов, как индексируемые поля, а также возрастающий и убывающий порядок;
  • <options>: дополнительная конфигурация, обычно используемая для указания характера индекса.

Для удобства следующей демонстрации сначала вставьте некоторые тестовые данные и создайте индекс для поля имени:

db.user.insertMany([
    {
        name: "heibai",
        age: 26,
        birthday: new Date(1998,08,23),
        createTime: new Timestamp(),
        Hobby: ["basketball", "football", "tennis"]
    },
    {
        name: "hei",
        age: 32,
        birthday: new Date(1989,08,23),
        createTime: new Timestamp(),
        Hobby: ["basketball", "tennis"]
    },
    {
        name: "ying",
        age: 46,
        birthday: new Date(1978,08,23),
        createTime: new Timestamp(),
        Hobby: ["tennis"]
    }
])


# 创建索引, -1表示以降序的顺序存储索引
db.user.createIndex( { name: -1 } )

1.2 Просмотр указателя

После создания индекса вы можете использоватьgetIndexes()Просмотр всей информации об индексе коллекции, например:

db.user.getIndexes()

Из вывода видно, что имя индекса по умолчанию: имя поля + сопоставление. Здесь в дополнение к индексу, который мы создали для поля имени, есть_idИндекс поля, который автоматически создается программой для предотвращения вставки одного и того же_idДокумент:

{
    "v" : 2,
    "key" : {
        "_id" : 1
    },
    "name" : "_id_",
    "ns" : "test.user"
},

{
    "v" : 2,
    "key" : {
        "name" : -1
    },
    "name" : "name_-1",
    "ns" : "test.user"
}

Тип индекса

В настоящее время MongoDB 4.x поддерживает следующие шесть типов индексов:

2.1 Индекс одного поля

Он поддерживает создание индекса для одного поля, что является самой простой формой индексации.Индекс, который мы создали выше для поля имени, является индексом для одного поля. Важно отметить, что когда мы создавали индекс для поля имени, мы указывали для него параметры сортировки. Но на практике в отсортированных запросах, включающих индексы с одним полем, порядок ключей индекса не имеет значения, поскольку MongoDB поддерживает обход индекса в любом направлении. можно использовать оба следующих запросаname_-1Сортировать по индексу:

db.user.find({}).sort({name:-1})
db.user.find({}).sort({name:1})

Большинство современных баз данных поддерживают индексы двунаправленного обхода, которые связаны со структурой хранения (как показано на рисунке ниже). В листовых узлах структуры B-дерева хранятся значение ключа индекса и информация о местоположении соответствующего документа, и каждый листовой узел подобен двусвязному списку, который можно просматривать спереди назад или от начала до конца. задом наперед. :

2.2 Композитный индекс

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

db.user.createIndex( { name: -1,birthday: 1} )

Следует отметить, что составной индекс MongoDB имеет характеристики префиксного индекса, то есть если вы создаете индекс{ a:1, b: 1, c: 1, d: 1 }, то это эквивалентно наличию в наборе следующих трех индексов.Эти три неявных индекса также можно использовать для оптимизации запросов и операций сортировки:

{ a: 1 }
{ a: 1, b: 1 }
{ a: 1, b: 1, c: 1 }

Поэтому следует стараться избегать создания избыточных индексов, что приведет к дополнительным потерям производительности. то есть, если вы создаете индекс{ name: -1, birthday: 1}, затем создайте{name:-1}Индексы создаются избыточно.

Также необходимо обратить внимание на ограничение составного индекса, такого как index.{a:1, b:-1}служба поддержки{a:1, b:-1}а также{a:-1, b:1}сортировать запросы формы, но не поддерживается{a: - 1, b:-1}или{a:1, b:1}сортировать запрос. То есть правила упорядочения полей либо в точности совпадают с правилами упорядочения ключей индекса, либо полностью противоположны им.

2.3 Многоключевой индекс

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

db.user.createIndex( { Hobby: 1 } )

2.4 Хэш-индекс

Для поддержки сегментирования на основе хэша MongoDB предоставляет хеш-индекс, который вычисляет позицию сегмента путем хеширования значения индекса. Синтаксис следующий:

db.collection.createIndex( { _id: "hashed" } )

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

2.5 Геопространственные индексы

Для поддержки эффективных запросов к данным геопространственных координат MongoDB предоставляет два специальных индекса:

  • Используйте двухмерную индексацию геометрии плоскости, в основном для данных плоской карты (например, данных игровой карты), непрерывных данных времени;
  • Индекс 2dsphere с использованием сферической геометрии, в основном для фактических данных сферической карты.

Эти данные обычно используются для решения фактического географического запроса, такого как близлежащие продукты питания, все предприятия в диапазоне запросов и других функций. Его синтаксис создания выглядит следующим образом:

db.<collection>.createIndex( { <location field> : "2d" ,
                               <additional field> : <value> } ,
                             { <index-specification options> } )
db.collection.createIndex( { <location field> : "2dsphere" } )

2.6 Текстовый указатель

MongoDB поддерживает полнотекстовое индексирование для полнотекстового поиска содержимого указанных полей. Синтаксис его создания следующий:

db.<collection>.createIndex( { field: "text" } )

Следует отметить, что коллекция может иметь не более одного текстового индекса, но текстовый индекс может содержать несколько полей, синтаксис следующий:

db.<collection>.createIndex(
   {
     field0: "text",
     field1: "text"
   }
 )

Создание текстового индекса — очень затратная операция, так как при создании текстового индекса необходимо выполнить семантический анализ и эффективное разбиение текста, а также необходимо хранить ключевые слова разбиения в памяти, что влияет на вычислительную мощность и объем памяти. устройства.Требования к MongoDB очень высоки, а также снижают производительность, поэтому его нужно использовать с осторожностью.

В-третьих, характер индекса

При создании индекса вы можете передать второй параметр<options>Используется для указания свойств индекса. Наиболее часто используемые свойства индекса:

3.1 Уникальный индекс

Уникальный индекс гарантирует, что значение столбца с уникальным индексом встречается только один раз в одной и той же коллекции. Пример выглядит следующим образом:

db.user.createIndex( { name: -1,birthday: 1}, { unique: true })

В этот момент выполнение следующей операции сообщит об ошибке, потому чтоname = heibaiа такжеbirthday = new Date(1998,08,23)Данные уже существуют:

db.user.insertOne({
        name: "heibai",
        birthday: new Date(1998,08,23)
})

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

db.user.insertOne({
        age: 12
})

Чтобы решить эту проблему, вам нужно использовать разреженность индекса.

3.2 Разреженность

Чтобы решить вышеуказанную проблему, нам нужно добавить разреженность в индекс. Поскольку индекс нельзя изменить, указанный выше индекс можно только сначала удалить, а затем создать и указать для него.sparseСвойство истинно, и конкретный оператор создания выглядит следующим образом:

db.user.dropIndex("name_-1_birthday_1")
db.user.createIndex( { name: -1,birthday: 1}, { unique: true,sparse: true})

На этом этапе вы можете выполнить приведенный выше оператор вставки несколько раз, чтобы вставить успешно. Причина в том, что для разреженного индекса он содержит информацию индекса только для документов с индексированным полем, даже если значение индексированного поля равно null, но соответствующее индексированное поле не может отсутствовать. В случае отсутствия соответствующий документ не будет включен в информацию индекса.

3.3 Частичный указатель

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

  • Выражения уравнения (т.е.字段: 值или используйте оператор $eq);
  • $exists: trueвыражение;
  • gt、гте,lt、Оператор LTE;
  • $ ТИП оператора;
  • Оператор $and на верхнем уровне.

Пример использования следующий:

db.user.createIndex(
   { name: -1 },
   { partialFilterExpression: { age: { $gt: 30 } } }
)

3.4 TTL-индексы

Индексы TTL позволяют установить для каждого документа тайм-аут, по истечении которого документ удаляется. Срок действия индекса TTL равен значению поля индекса + указанное количество секунд. Значение поля индекса здесь может быть только типа Дата. Пример следующий:

db.user.createIndex( { "birthday": 1 }, { expireAfterSeconds: 60 } )

Здесь мы строим индекс TTL для поля дня рождения только в демонстрационных целях.На самом деле индекс TTL в основном используется для данных, которые необходимо сохранять только в течение определенного периода времени, таких как состояние сеанса, временные журналы и т. д. При использовании индексов TTL следует помнить и о других вещах:

  • Свойство TTL можно использовать только для индексов с одним полем, составные индексы не поддерживаются.
  • Тип поля для установления индекса TTL может быть только типа Дата, а тип метки времени не допускается.
  • Если поле представляет собой массив и в индексе есть несколько значений даты, MongoDB использует самое раннее значение даты в массиве для расчета времени истечения срока действия.
  • Если индексированное поле в документе не является датой или массивом, содержащим значения дат, срок действия документа не истечет.
  • Если документ не содержит индексированного поля, срок действия документа не истекает.

В-четвертых, удалите индекс

Синтаксис удаления индекса относительно прост, просто вызовитеdropIndexметод, вы можете передать имя индекса или определение индекса, примеры следующие:

db.user.dropIndex("name_-1")
db.user.dropIndex({ name: -1,birthday: 1})

Если вы хотите удалить все индексы, вы можете позвонитьdropIndexesметод, следует отметить, что_idИндекс по умолчанию не удаляется.

db.collection.dropIndexes()

Кроме того, эта команда установит блокировку записи соответствующей базы данных и заблокирует другие операции до завершения удаления индекса.

5. ОБЪЯСНИТЕ

5.1 Выходные параметры

MongoDBexplain()Этот метод аналогичен ключевому слову объяснения MySQL, которое используется для отображения релевантной информации о плане выполнения. Пример выглядит следующим образом:

db.user.find({name:"heibai"},{name:1,age:1}).sort({ name:1}).explain()

Частичный вывод плана выполнения на этом этапе выглядит следующим образом:

"inputStage" : {
    "stage" : "FETCH",
    "inputStage" : {
        "stage" : "IXSCAN",
        "keyPattern" : {
            "name" : -1,
            "birthday" : 1
        },
        "indexName" : "name_-1_birthday_1",
        "isMultiKey" : false,
        "multiKeyPaths" : {
            "name" : [ ],
            "birthday" : [ ]
        },
        "isUnique" : true,
        "isSparse" : true,
        "isPartial" : false,
        "indexVersion" : 2,
        "direction" : "backward",
        "indexBounds" : {
            "name" : [
                "[\"heibai\", \"heibai\"]"
            ],
            "birthday" : [
                "[MaxKey, MinKey]"
            ]
        }
    }
}

Внутренний слой в выходном результатеinputStage.stageценностьIXSCAN, что означает, что индекс используется для сканирования в это время, иindexNameПоле показывает, что соответствующий индексname_-1_birthday_1. внешний слойinputStage.stageценностьFETCH, а это значит, что помимо получения данных из индекса, вам также необходимо получить данные из соответствующего документа, потому что информация о возрасте не хранится в индексе. Эти выходные данные доказывают, что MongoDB поддерживает префиксные индексы, а индексы с одним ключом поддерживают двунаправленное сканирование.

5.2 Покрывающие индексы

Здесь мы немного модифицируем приведенный выше оператор запроса, не возвращая поле возраста и значение по умолчанию._idполе, утверждение выглядит следующим образом:

db.user.find({name:"heibai"},{_id:0, name:1}).sort({ name:1 }).explain()

Вывод следующий. Вы можете видеть, что в запросе отсутствует одинFETCHсцена. Это означает, что вам нужно только просмотреть индекс, чтобы получить всю необходимую информацию.name_-1_birthday_1Индекс является покрывающим индексом для этой операции запроса.

"inputStage" : {
    "stage" : "IXSCAN",
    "keyPattern" : {
        "name" : -1,
        "birthday" : 1
    },
    "indexName" : "name_-1_birthday_1",
    "isMultiKey" : false,
    "multiKeyPaths" : {
        "name" : [ ],
        "birthday" : [ ]
    },
    "isUnique" : true,
    "isSparse" : true,
    "isPartial" : false,
    "indexVersion" : 2,
    "direction" : "backward",
    "indexBounds" : {
        "name" : [
            "[\"heibai\", \"heibai\"]"
        ],
        "birthday" : [
            "[MaxKey, MinKey]"
        ]
    }
}

использованная литература

  1. Официальная документация:Indexes,sort-on-multiple-fields
  2. Кристина Ходоров, Полное руководство по MongoDB (2-е издание), People's Mail Press, 2014-01

Дополнительные статьи см. в [Full Stack Engineer Manual], адрес GitHub:GitHub.com/Black and WhiteShould/…