Прискорення мобільної БД: індекси, 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 проєктів з оптимізацією БД. Ми гарантуємо вимірний приріст продуктивності. Зв'яжіться з нами, щоб отримати консультацію та розрахунок вартості. Замовте аудит вже сьогодні та отримайте детальний звіт з рекомендаціями.