Влияние количества индексов на запись

В основе работы Dexie.js лежит IndexedDB, где каждая запись в хранилище сопровождается обновлением всех связанных индексов. Индексы в IndexedDB представляют собой отдельные структуры данных (B-tree), которые поддерживают упорядоченный доступ к данным по заданным полям. Любое изменение объекта в хранилище автоматически приводит к пересчёту значений во всех индексах, затронутых схемой.

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


Архитектура обновления индексов при записи

При выполнении операции add, put или update в Dexie.js происходит последовательность действий:

  • сериализация объекта;
  • запись в основное хранилище (object store);
  • вычисление значений всех индексов;
  • вставка или обновление ключей в соответствующих B-tree структурах;
  • поддержание целостности ссылок между primary key и индексами.

Каждый индекс добавляет дополнительный проход логики вставки. Даже если индекс не используется в запросах, он всё равно участвует в каждой операции записи.


Стоимость операций записи при увеличении числа индексов

Рост количества индексов оказывает прямое влияние на:

Время выполнения записи

Каждый индекс требует отдельной операции вставки. Внутри IndexedDB это означает логарифмическую сложность для каждой структуры:

  • одна запись без индексов: O(log n)
  • k индексов: O(k × log n)

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


Потребление CPU

Обновление индексов требует:

  • вычисления ключей (включая составные индексы);
  • сравнения значений;
  • балансировки B-tree;
  • обработки уникальности (unique constraints).

Особенно заметно влияние при использовании:

  • составных индексов ([a+b]);
  • уникальных индексов;
  • multiEntry индексов.

Задержки в транзакциях

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

  • блокировке чтения в пределах той же области;
  • увеличению latency при конкурентных операциях;
  • росту вероятности конфликтов транзакций.

Сложность поддержания индексных структур

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

При большом количестве индексов возникают дополнительные эффекты:

  • фрагментация B-tree узлов;
  • рост операций перераспределения узлов при балансировке;
  • увеличение количества disk writes даже при использовании кэширования браузера.

Влияние составных индексов

Составные индексы в Dexie.js (db.table('users').schema.index('a+b')) увеличивают нагрузку сильнее, чем простые индексы.

Причины:

  • формирование ключа требует объединения значений;
  • сравнение выполняется по лексикографическому принципу;
  • индекс обновляется даже при изменении только одного из полей;
  • увеличивается вероятность пересоздания записи в B-tree.

При нескольких составных индексах один и тот же объект может многократно участвовать в пересчёте ключей.


MultiEntry индексы и их стоимость

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

Например:

tags: ['js', 'indexeddb', 'dexie']

Приводит к созданию трёх индексных записей.

Это приводит к:

  • экспоненциальному росту числа индексных вставок при больших массивах;
  • увеличению времени записи пропорционально размеру массива;
  • росту объёма индексного хранилища.

Влияние индексов на пропускную способность записи

При высокой частоте операций записи основным ограничением становится не основное хранилище, а индексы.

Типичная деградация проявляется следующим образом:

  • 1–2 индекса: минимальное влияние;
  • 3–5 индексов: заметное увеличение latency;
  • 6–10 индексов: резкое падение throughput;
  • более 10 индексов: транзакции становятся узким местом системы.

Эти границы зависят от объёма данных и сложности ключей, но тенденция сохраняется.


Кэширование и оптимизации внутри Dexie.js

Dexie.js не устраняет стоимость индексов, однако снижает накладные расходы за счёт:

  • группировки операций в транзакции;
  • минимизации повторных вычислений ключей внутри одной batch-операции;
  • оптимизации последовательных вставок.

Тем не менее, все индексы всё равно обновляются синхронно с записью.


Компромисс между скоростью записи и скоростью чтения

Индексы ускоряют чтение, но замедляют запись. Это фундаментальный trade-off IndexedDB:

  • без индексов: быстрые записи, медленные выборки;
  • с индексами: быстрые выборки, медленные записи.

Dexie.js усиливает эту модель, так как предоставляет удобный декларативный слой, но не изменяет физику IndexedDB.


Типовые ошибки проектирования схемы

Чрезмерное количество индексов часто возникает из-за попытки оптимизировать каждый возможный запрос.

Характерные проблемы:

  • создание индекса под каждый фильтр;
  • дублирование индексов (например, a и a+b, где a уже покрыт частично);
  • индексация редко используемых полей;
  • сохранение индексов для аналитических, а не оперативных запросов.

Такая схема приводит к росту стоимости записи без пропорциональной выгоды.


Эффект накопления при массовых вставках

При пакетных операциях (bulkAdd, bulkPut) влияние индексов становится особенно заметным.

Каждый объект в массиве:

  • проходит через полный цикл индексирования;
  • участвует в обновлении всех B-tree структур;
  • увеличивает нагрузку на транзакцию линейно.

Итоговая стоимость операции определяется выражением:

  • O(m × k × log n), где m — количество записей, k — количество индексов, n — размер таблицы.

Ограничения масштабирования

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

  • увеличивается размер IndexedDB хранилища;
  • растёт время открытия базы (rebuild metadata);
  • увеличивается стоимость миграций схемы;
  • повышается нагрузка на garbage collection при работе с промежуточными объектами.

Практика балансировки схемы индексов

Оптимальная схема индексов обычно стремится к минимальному набору, покрывающему основные паттерны чтения:

  • индексы под ключевые фильтры;
  • ограниченное число составных индексов;
  • исключение дублирующих индексов;
  • отказ от индексов под редкие запросы.

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


Накопительный эффект при длительной работе приложения

При длительной эксплуатации IndexedDB влияние индексов усиливается:

  • B-tree структуры увеличиваются;
  • балансировка требует больше операций;
  • кэш браузера перестаёт компенсировать стоимость записи;
  • фрагментация индексов приводит к дополнительным disk writes.

Это делает количество индексов критическим параметром долговременной производительности.