Ускорение мобильной БД: индексы, N+1, фоновые контексты

Почему N+1 запросы убивают производительность? Мы часто сталкиваемся с ситуацией, когда загружается список заказов, а потом для каждого выполняется отдельный запрос за пользователем. 100 заказов = 101 запрос к SQLite. Это классический **N+1 запрос**. При 1000 записей это 1001 запрос, каждый из ко

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

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

Услуги, которые мы предлагаем
Показано 1 из 1Все 1734 услуг
Ускорение мобильной БД: индексы, N+1, фоновые контексты
Средний
~2-3 дня

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    597

Почему N+1 запросы убивают производительность?

Мы часто сталкиваемся с ситуацией, когда загружается список заказов, а потом для каждого выполняется отдельный запрос за пользователем. 100 заказов = 101 запрос к SQLite. Это классический N+1 запрос. При 1000 записей это 1001 запрос, каждый из которых может занимать 5–10 мс, итого 5–10 секунд блокировки UI. CoreData решает через relationshipKeyPathsForPrefetching, Room — через @Relation с @Transaction. Flutter + sqflite — JOIN-запрос вместо вложенного цикла. Типичная ошибка: разработчики не смотрят на количество запросов в логе. Результат — приложение тормозит, а проблема в нескольких строках кода.

Как индексы ускоряют запросы в 10 раз?

SQLite под капотом у CoreData, Room и большинства мобильных ORM. WHERE по неиндексированному полю на таблице с 50 000 строк делает full scan. На Android Room добавляем @Index к сущности, на iOS CoreData — выставляем indexed в Data Model Inspector. Разница в скорости выборки — десятки раз. Без индекса запрос может длиться 500 мс, с индексом — 5 мс. Типичный пример: поиск товара по названию на таблице из 200 000 строк без индекса занимает 1.8 секунды, с индексом — 40 мс. Всегда добавляйте индексы на поля, участвующие в WHERE, JOIN и ORDER BY.

Решения по платформам

iOS — CoreData

NSPersistentContainer даёт newBackgroundContext() для фоновых операций. Правильная схема использует фоновые контексты:

container.performBackgroundTask { context in // массовые операции здесь try? context.save() DispatchQueue.main.async { // обновление UI } } 

NSFetchRequest.fetchBatchSize = 20 — CoreData загружает данные порциями по мере обращения, а не всё сразу. NSFetchedResultsController с sectionNameKeyPath для таблиц с секциями — правильный паттерн, который автоматически обновляет UITableView при изменении данных.

Для bulk insert NSBatchInsertRequest (iOS 13+) работает напрямую в SQLite без создания managed objects — в 10–20 раз быстрее стандартного insert для тысяч записей.

Android — Room

@Query с EXPLAIN QUERY PLAN через adb shell — быстрый способ увидеть, есть ли full scan. Room @TypeConverter для JSON-полей через Gson / Moshi работает, но тормозит на массовых выборках — нормализуйте данные.

Flow<List<Entity>> из Room автоматически эмитит новые данные при изменении таблицы — не нужно вручную инвалидировать кэш. distinctUntilChanged() предотвращает лишние эмиссии если данные не изменились.

Room.databaseBuilder().setQueryCoroutineContext(Dispatchers.IO) — явно указываем, что Room-запросы идут на IO-диспетчере.

Flutter — Drift

Drift (бывший Moor) — предпочтительный выбор для сложных схем: типобезопасные запросы, миграции, генерация кода. database.transaction() для batch-операций — в транзакции 1000 INSERT выполняются за 50–100 мс, без транзакции — 5–10 секунд (каждый INSERT открывает/закрывает транзакцию SQLite).

Как FTS помогает при поиске по 200 000 записей?

Из нашей практики: приложение для offline-каталога товаров — поиск по названию на Room без FTS занимал 1.8 секунды. Подключили FTS4:

@Fts4 @Entity(tableName = "products_fts") data class ProductFts(val name: String, val description: String) 

MATCH по FTS-таблице — 40–60 мс на том же датасете. Как отмечают разработчики SQLite, FTS4 обеспечивает полнотекстовый поиск за миллисекунды. Для iOS CoreData используйте NSPredicate с MATCH через SQLite напрямую, если нужно быстрый полнотекстовый поиск.

Как правильно оптимизировать базу данных мобильного приложения?

Шаг 1: Аудит схемы

Выявите таблицы без индексов и проблемные запросы с помощью EXPLAIN QUERY PLAN.

Шаг 2: Добавление индексов

На поля, участвующие в WHERE, JOIN и ORDER BY, создайте индексы.

Шаг 3: Устранение N+1 запросов

Используйте prefetching в CoreData или JOIN в Room/Drift.

Шаг 4: Batch-операции

Для массовых вставок применяйте NSBatchInsertRequest, @Insert с аннотацией в Room или транзакции в Drift.

Шаг 5: Тестирование производительности

Замерьте время запросов до и после, убедитесь в отсутствии блокировок UI.

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

Запрос До оптимизации После оптимизации
Поиск по названию (200k записей) 1.8 с (LIKE) 40 мс (FTS4)
Загрузка списка заказов с пользователями (N+1) 1.1 с (101 запрос) 20 мс (1 JOIN)
Массовая вставка 10 000 записей 15 с (по одной) 600 мс (batch)
Пример кода для batch-вставки в Drift
await database.transaction(() async { for (final item in items) { await database.insert(item); } }); 

Что входит в оптимизацию?

Этап Что делаем Длительность
Аудит схемы Анализ таблиц, индексов, запросов 1-2 дня
Оптимизация Индексы, batch-операции, фоновые контексты 3-7 дней
Тестирование Проверка производительности и регрессии 1-2 дня
Документация Рекомендации по мониторингу включено

Наш опыт — 5 лет на рынке мобильной разработки, более 50 проектов с оптимизацией БД. Мы гарантируем измеримый прирост производительности. Свяжитесь с нами, чтобы получить консультацию и расчёт стоимости. Закажите аудит уже сегодня и получите детальный отчёт с рекомендациями.