localForage строится как единый API поверх нескольких механизмов
хранения: IndexedDB, WebSQL и localStorage. Абстракция скрывает
различия, но стоимость операций чтения и записи определяется не
библиотекой, а конкретным драйвером и его внутренней моделью работы с
данными.
Стоимость операции в данном контексте включает:
- время сериализации и десериализации значений;
- накладные расходы на транзакции;
- стоимость обращения к диску или синхронному хранилищу;
- задержки event loop и очередей задач;
- ограничения на блокировки потока выполнения.
localStorage:
синхронные операции и скрытая дороговизна
localStorage является самым простым, но одновременно самым проблемным
по производительности драйвером.
Стоимость записи
Операция записи в localStorage включает:
- синхронную сериализацию значения в строку (обычно
JSON.stringify);
- синхронную запись в память браузера;
- немедленную блокировку основного потока.
Ключевой фактор стоимости — блокировка UI thread. Даже небольшие
объёмы данных приводят к заметным задержкам, поскольку:
- запись выполняется синхронно;
- отсутствует транзакционная модель;
- каждое изменение вызывает перерасчёт внутреннего хранилища
браузера.
При увеличении объёма данных стоимость растёт нелинейно, так как
многие браузеры пересобирают внутренний storage snapshot целиком.
Стоимость чтения
Чтение также синхронно:
- извлечение строки из памяти;
- JSON.parse;
- возврат результата.
Основная нагрузка возникает на этапе десериализации, особенно
при:
- глубоко вложенных структурах;
- больших массивах;
- частых вызовах getItem.
Особенности
- отсутствует асинхронность;
- нет транзакций;
- высокая стоимость при массовых операциях;
- быстрый старт, но плохая масштабируемость.
IndexedDB:
асинхронная модель с транзакционной стоимостью
IndexedDB — основной драйвер localForage в современных браузерах. Он
обеспечивает наилучший баланс между производительностью и
масштабируемостью, но имеет более сложную модель стоимости операций.
Стоимость записи
Запись в IndexedDB включает несколько этапов:
- создание или использование транзакции;
- структурное клонирование объекта (structured clone algorithm);
- запись в B-tree структуру хранилища;
- флаш транзакции на диск (не всегда мгновенный).
Главная статья расходов — structured clone. Этот процесс:
- создаёт глубокую копию объекта;
- сериализует поддерживаемые типы (ArrayBuffer, Date, Map и др.);
- может быть существенно медленнее JSON-сериализации для простых
структур;
- увеличивает стоимость пропорционально глубине и размеру
объекта.
Дополнительные затраты:
- создание транзакции (особенно при частых одиночных записях);
- блокировки object store на время транзакции;
- дисковая синхронизация при commit.
Стоимость чтения
Чтение состоит из:
- открытия транзакции;
- поиска ключа в B-tree индексе;
- восстановления объекта через structured clone обратно в
JS-объект.
Основные затраты:
- поиск в индексе (логарифмическая сложность);
- десериализация structured clone;
- асинхронный переход через event loop.
Чтение обычно быстрее записи, поскольку отсутствует необходимость
модификации структуры хранения, но при больших объектах стоимость clone
остаётся значительной.
WebSQL:
устаревшая, но предсказуемая модель стоимости
WebSQL основан на SQLite и использует SQL-запросы для работы с
данными. Хотя технология устарела, её поведение всё ещё встречается в
некоторых WebView.
Стоимость записи
Операция записи включает:
- выполнение SQL INSERT/UPDATE;
- сериализацию данных (часто в TEXT/BLOB);
- блокировки таблиц или страниц SQLite;
- запись WAL (Write-Ahead Logging) или прямую модификацию файла.
Стоимость зависит от:
- сложности схемы;
- наличия индексов;
- размера BLOB.
Преимущество WebSQL — предсказуемая дисковая модель: стоимость растёт
более линейно, чем в localStorage, и менее зависима от JS-объектов.
Стоимость чтения
Чтение включает:
- выполнение SQL SELECT;
- использование индексов;
- десериализацию результата.
Основные затраты:
- парсинг SQL;
- доступ к файлу базы;
- преобразование строк/Blob в JS-структуры.
WebSQL часто быстрее localStorage при больших объёмах данных, но
уступает IndexedDB по параллелизму.
Сравнение стоимости
операций по драйверам
Запись одного объекта
- localStorage: минимальные накладные расходы, но полная блокировка
потока
- IndexedDB: высокая стоимость structured clone, но неблокирующая
модель
- WebSQL: средняя стоимость с дисковой записью и SQL overhead
Чтение одного объекта
- localStorage: очень быстро для малых данных, но синхронная
блокировка
- IndexedDB: асинхронный доступ с B-tree поиском
- WebSQL: SQL-запрос + доступ к файлу
Массовые операции записи
localStorage
Массовая запись резко ухудшает производительность:
- каждая операция блокирует main thread;
- браузер может пересобирать storage snapshot;
- отсутствует batching на уровне API.
Стоимость растёт пропорционально количеству вызовов без
оптимизации.
IndexedDB
Поддерживает эффективные батчи через транзакции:
- одна транзакция на множество записей значительно дешевле;
- уменьшается overhead создания transaction;
- disk flush может быть отложен браузером.
Ключевой фактор — группировка операций.
WebSQL
SQL позволяет:
- использовать batch INSERT;
- выполнять транзакции на уровне SQL BEGIN/COMMIT;
- минимизировать дисковые операции.
Стоимость сериализации и
клонирования
localForage не использует единый формат хранения, но все драйверы
сталкиваются с преобразованием JS-объектов.
JSON-сериализация
(localStorage)
- линейная сложность O(n);
- высокая скорость для простых объектов;
- деградация при циклических или вложенных структурах (через обход
ограничений).
Structured Clone (IndexedDB)
- поддерживает сложные типы данных;
- дороже JSON.stringify для простых структур;
- эффективнее для бинарных данных и сложных объектов.
Влияние размера данных на
стоимость
Малые объекты (до нескольких
КБ)
- localStorage: минимальная стоимость
- IndexedDB: overhead транзакции может быть выше полезной работы
- WebSQL: сопоставим с IndexedDB
Средние объекты (десятки-сотни
КБ)
- IndexedDB начинает выигрывать за счёт асинхронности
- localStorage становится заметно медленным
- WebSQL сохраняет стабильность
Большие объекты (МБ и выше)
- localStorage практически непригоден
- IndexedDB оптимален благодаря потоковой архитектуре браузера
- WebSQL зависит от реализации SQLite и может деградировать при
блокировках
Транзакционная стоимость
IndexedDB
IndexedDB вводит отдельную категорию стоимости — управление
транзакциями.
Каждая транзакция включает:
- создание контекста;
- блокировку object store;
- очередь операций;
- commit/abort стадии.
Стоимость возрастает при:
- множественных коротких транзакциях;
- конкуренции за один object store;
- частых read/write interleaving.
Оптимальная модель — минимизация числа транзакций при сохранении
логической целостности операций.
Стоимость конкурентного
доступа
localStorage
- отсутствует конкурентная модель;
- изменения синхронны;
- возможны race conditions между вкладками через storage events.
IndexedDB
- поддерживает параллельные транзакции;
- блокировка на уровне object store;
- конфликтность приводит к queueing операций.
WebSQL
- блокировки таблиц;
- последовательное выполнение SQL в транзакциях.
Энергетическая и системная
стоимость
Хотя браузеры не предоставляют прямых метрик энергопотребления,
косвенно:
- localStorage увеличивает CPU load из-за блокировки main thread;
- IndexedDB снижает нагрузку на UI, но увеличивает I/O
активность;
- WebSQL создаёт равномерную нагрузку на диск и CPU.
Итоговая модель оценки
стоимости
Стоимость операции можно формализовать как сумму компонентов:
- T_serialization — сериализация данных;
- T_transaction — создание и управление транзакцией;
- T_io — дисковая или memory I/O операция;
- T_clone — структурное копирование;
- T_eventloop — задержки планировщика задач.
Для каждого драйвера распределение этих компонентов различается:
- localStorage: доминирует T_serialization + блокировка
T_eventloop;
- IndexedDB: доминирует T_clone + T_transaction;
- WebSQL: доминирует T_io + SQL parsing overhead.
Поведенческие
последствия различий стоимости
Различия в стоимости операций напрямую влияют на архитектуру
приложений:
- частые мелкие записи становятся критичными для localStorage;
- IndexedDB требует оптимизации транзакционных границ;
- WebSQL требует минимизации SQL round-trips.
Эти различия определяют не только производительность, но и
устойчивость приложения под нагрузкой, особенно при активной работе с
offline-first сценариями и синхронизацией состояния.