Понимание индекса MongoDB

Индекс MongoDB: глубокое погружение, понимание индексов.

Повышение производительности за счет оптимального использования индексов за счет понимания структуры данных, того, как они работают / хранятся, как загружаются в память. Как оптимизация запросов принимает решение о выборе индексов.

Требуется базовое понимание индексов, т.е. что такое индексы, типы индексов, их создание. Https://docs.mongodb.com/manual/indexes/

  • Структура данных
  • Хранение на диске
  • Выделение памяти.

Структура данных

Индекс в поле / полях сохраняется в порядке, который мы указываем с помощью структуры данных B-Tree. Сохранено в порядке. Посмотрим, что это означает и чем помогает.

  • Индекс создается на основе значения поля, относящегося к фактическому сохраненному документу.

  • Использование индексов B-Tree значительно сокращает количество сравнений для поиска документа.

  • Аналогично на рисунке ниже мы видим, что с индексом (голубая линия) даже добавление документа все еще ограничивает количество проверяемых документов по сравнению с без индекса / сканирования.

Хранилище на диске

Давайте посмотрим / визуализируем, как индекс хранится на диске. Индекс, хранящийся на диске, управляется самим механизмом хранения базы данных.

  • Использует сжатие индекса префикса. Повторяющееся значение префикса не записывается. Давайте посмотрим на примере, чтобы понять, что оно означает.
db.getCollection("movieTicket")
.ensureIndex({"showDate":1, "seatNo":1, "status":1});

Как индекс ({«showDate»: 1, «seatNo»: 1, «status»: 1}) сохраняется на диске.

Как индекс ({«seatNo»: 1, «showDate»: 1, «status»: 1}) сохраняется на диске.

Размер составного индекса может варьироваться в зависимости от положения поля индекса (плотское / избирательное).
Индекс: «seatNostorage.wiredTiger.engineConfig.cacheSizeGBshowDatestorage.wiredTiger.engineConfig.cacheSizeGBstatus_1» займет больше места, чем index: «showDatestorage.wiredTiger.engineConfig.cacheSizeGBseatNostorage.wiredTiger.engineConfig.cacheSizeGBstatus_1 ».

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

Выделение памяти

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

Под памятью здесь подразумевается выделенная для MongoDB память, а не общий объем ОЗУ / памяти на машине.
Чтобы настроить размер внутреннего кеша WiredTiger, см. storage.wiredTiger.engineConfig.cacheSizeGB и
--wiredTigerCacheSizeGB . Избегайте увеличения размера внутреннего кэша WiredTiger выше значения по умолчанию.

//To find Internal memory allocated to wiredTiger.cache
db.serverStatus().wiredTiger.cache ; 
"maximum bytes configured" : 16586375168
//Find the system memory
db.hostInfo();
{
 "system" : {
 ..
  "memSizeMB" : 32660,
  "memLimitMB" : 32660,
  "numCores" : 8,
....
 },
 ...
}

В лучшем случае индекс полностью помещается в память

Но на тот случай, если не влезет в память.

Затем проиндексированные страницы просматриваются, из них удаляются из памяти, а самые свежие загружаются в память.

В сценарии, когда в используемом индексе происходит непрерывный обход, то есть ключ индекса распределяется по всем страницам для значения поля, в котором выполняется запрос. [Индекс не полностью помещается в память].
Это приведет к большому количеству страниц ввода / вывода с использованием Disk I / 0, что сильно повлияет на производительность запроса / индекса. [см. рисунок ниже]

Ex- для запроса ниже.

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

db.movieTicket.find({"showDate":"", "seatNo": {$in: ['A1', 'A2', 'A3', 'A4']}}).hint({"showDate": 1, "seatNo": 1, "status": 1});

Отдельная страница будет считана в память.

db.movieTicket.find({"showDate":"", "seatNo": {$in: ['A1', 'A2', 'A3', 'A4']}}).hint({"seatNo": 1, "showDate": 1, "status": 1});

В память будут прочитаны несколько страниц.

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

// DB stats with indexDetails gives lot's of detailed information.
var stats = db.movieTicket.stats({indexDetails:true});
// will just look for cache Information
stats.indexDetails.showDatestorage.wiredTiger.engineConfig.cacheSizeGBseatNostorage.wiredTiger.engineConfig.cacheSizeGBstatus_1.cache;
stats.indexDetails.seatNostorage.wiredTiger.engineConfig.cacheSizeGBshowDatestorage.wiredTiger.engineConfig.cacheSizeGBstatus_1.cache;
// OUTPUT - 
{
 "bytes currently in the cache" : 210148,
 "bytes dirty in the cache cumulative" : 0,
 "bytes read into cache" : 79139,
 "bytes written from cache" : 0,
 "checkpoint blocked page eviction" : 0,
 "data source pages selected for eviction unable to be evicted" : 0,
 "eviction walk passes of a file" : 0,
 "eviction walk target pages histogram - 0-9" : 0,
 "eviction walk target pages histogram - 10-31" : 0,
 "eviction walk target pages histogram - 128 and higher" : 0,
 "eviction walk target pages histogram - 32-63" : 0,
 "eviction walk target pages histogram - 64-128" : 0,
 "eviction walks abandoned" : 0,
 "eviction walks gave up because they restarted their walk twice" : 0,
 "eviction walks gave up because they saw too many pages and found no candidates" : 0,
 "eviction walks gave up because they saw too many pages and found too few candidates" : 0,
 "eviction walks reached end of tree" : 0,
 "eviction walks started from root of tree" : 0,
 "eviction walks started from saved location in tree" : 0,
 "hazard pointer blocked page eviction" : 0,
 "in-memory page passed criteria to be split" : 0,
 "in-memory page splits" : 0,
 "internal pages evicted" : 0,
 "internal pages split during eviction" : 0,
 "leaf pages split during eviction" : 0,
 "modified pages evicted" : 0,
 "overflow pages read into cache" : 0,
 "page split during eviction deepened the tree" : 0,
 "page written requiring cache overflow records" : 0,
 "pages read into cache" : 6,
 "pages read into cache after truncate" : 0,
 "pages read into cache after truncate in prepare state" : 0,
 "pages read into cache requiring cache overflow entries" : 0,
 "pages requested from the cache" : 70,
 "pages seen by eviction walk" : 0,
 "pages written from cache" : 0,
 "pages written requiring in-memory restoration" : 0,
 "tracked dirty bytes in the cache" : 0,
 "unmodified pages evicted" : 0
}

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

Ссылки:

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

Удачного индексирования, спасибо :)