Оптимізація бази даних мобільного застосунку: де ламається швидкість
Локальне сховище — SQLite або обгортка над ним — стає вузьким місцем у шарі даних раніше, ніж мережа й відмальовування.
Поки записів десятки, усе відгукується миттєво; на реальних обсягах затримка перед відмальовуванням екрана зростає в рази, а підвисання під час прокручування списку зазвичай списують на важкі зображення.
Перша причина повільних вибірок у застосунку — індекси, яких немає під фактичні умови. Фільтр і сортування йдуть по стовпцях поза індексом, рушій перебирає всю таблицю, а вибірка всіх стовпців замість потрібних тягне зайві дані з диска.
На списку з кількох тисяч рядків такий запит виконується в рази довше й повторюється щоразу при відкритті екрана.
Друга — зайві звернення в циклі, класичний N+1. Адаптер списку запитує пов'язані дані по кожному елементу окремо: сотня карток перетворюється на сотню запитів, кожен зі своєю транзакцією й підготовкою виразу. Один пакетний запит за набором ідентифікаторів вирішує те саме завдання за одне звернення.
Третя — робота зі сховищем в основному потоці. Синхронне читання перед відмальовуванням, міграція або запис журналу під час старту блокують інтерфейс, і користувач бачить завмерлий екран.
Читання виноситься у фонові контексти, запис — у пакетні транзакції, а важкі агрегати обчислюються заздалегідь.
Шукати ці місця навмання безглуздо: оптимізація бази даних мобільного застосунку починається з вимірювань — план запиту, час до першого кадру, кількість звернень на один екран. Нижче розбираємо, як це закривається покроково.
Вимірюваний ефект: відгук екранів, пам'ять і автономність
Вимірюємо п'ять величин до й після: час відкриття розділу, плавність прокручування, швидкість відгуку екранів, обсяг локального сховища й витрату заряду у фоні.
Сценарії фіксовані — холодний старт, прогрітий кеш, прокручування списку на кілька тисяч записів — і знімаються на живих пристроях, а не в емуляторі, інакше цифри між прогонами непорівнювані.
Основний внесок дає перебудова роботи зі вбудованою SQLite. Каскад запитів на кожен рядок списку замінюється одним з'єднанням, під часті вибірки додаються складені індекси, важкі читання переїжджають у фоновий контекст.
Час відкриття розділу скорочується з 1,5 секунди до 0,2, прокручування йде без пропущених кадрів навіть на слабких пристроях.
Обсяг сховища перестає зростати, коли в кеша з'являється політика зберігання: старі записи архівуються й стискаються, проміжні вибірки не осідають на диск.
Витрата заряду у фоні падає після групування фонових завдань — застосунок перестає прокидатися кожні кілька секунд заради синхронізації.
Результат фіксуємо у звіті: по кожному екрану вимірювання до й після, окремо для холодного й прогрітого старту. Видно, які правки дали внесок і який запас ще залишився.
Варіанти прискорення: індексування, схема зберігання, фонові операції
Прискорення майже завжди впирається не в рушій, а в те, як застосунок з ним розмовляє. Більшість мобільних баз — це SQLite або об'єктна надбудова над ним, і запити, написані «як у серверному коді», на телефоні коштують у рази дорожче. Нижче — альтернативи, між якими реально обирають, та їхні обмеження.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Індексування | Повільні вибірки за конкретними полями, фільтри й сортування у списках | Уповільнює вставку й оновлення, займає місце у файлі бази; зайві індекси дають зворотний ефект |
| Зміна схеми зберігання | Дані збираються з багатьох пов'язаних таблиць, багато рідкісних полів | Потрібна міграція й версіонування схеми, під час першого запуску на великій базі — пауза |
| Пакетне завантаження даних | Екран робить десятки однотипних запитів у циклі замість одного | Потребує точних умов вибірки: занадто широка пачка тягне зайве |
| Кешування результатів | Часто відкриваються ті самі списки, агрегати перераховуються щоразу | Потрібна інвалідація за подіями запису, інакше користувач бачить застарілі дані |
| Фонові операції | Запис, синхронізація й важкі агрегати блокують інтерфейс | Потрібен контроль паралельного доступу до контексту запису й коректна обробка скасування |
Як це комбінують
На практиці підходи працюють разом: індекс закриває вибірку, пакетне завантаження прибирає цикл однотипних запитів, фоновий контекст переносить запис з головного потоку, кеш знімає повторні читання.
Індексування без перегляду схеми зберігання зазвичай дає лише частину ефекту, а винесення у фонові операції без індексів просто ховає ту саму затримку за іншим екраном.
Етапи робіт: від діагностики до передачі регламентів
Оптимізація бази даних мобільного застосунку без вимірювань перетворюється на вгадування: правки вносять «на око», а вузьке місце залишається на місці. Ми ведемо роботи послідовно — від точки відліку до регламенту, який утримує результат після релізу.
-
Діагностика вузьких місць. Знімаємо вимірювання на поточній збірці: холодний старт, відгук важких екранів, частота й тривалість звернень до локального сховища. Профіль зберігаємо як точку відліку — далі порівнюємо лише з ним.
-
Розбір звернень до даних. Проходимо по екранах і сценаріях, шукаємо N+1-запити, дублі читання, вибірки без індексів, роботу зі сховищем на головному потоці. Кожну підозру підтверджуємо профілем, а не здогадом.
-
План змін. Фіксуємо склад індексів, формулювання запитів, розмежування фонових контекстів, порядок міграції схеми. Погоджуємо до правок, щоб обсяг робіт і ризики були зрозумілі заздалегідь.
-
Налаштування. Вносимо зміни покроково, кожну — окремо й зі своїм вимірюванням: видно, що дало ефект, а що ні.
-
Перевірка на реальних даних. Прогоняємо сценарії на копії робочої бази й на слабких пристроях: холодний старт, великий обсяг історії, міграція з попередньої версії. Дивимося не середнє, а найгірший випадок.
-
Вимірювання до й після. Порівнюємо ті самі метрики з точкою відліку й показуємо різницю по кожному сценарію.
-
Регламент підтримки. Передаємо правила: як додавати індекси, коли потрібна міграція, які вимірювання обов'язкові перед релізом.
Що залишається у вас: артефакти, схеми й регламенти
Передаємо не звіт, а робочий комплект: з ним ваша команда розвиває застосунок і розбирає просідання продуктивності без нас. Усе зберігається поруч із кодом, щоб документація не відставала від змін схеми.
- Карта схеми зберігання — таблиці, зв'язки, ключі, обсяги й швидкість зростання по кожному сховищу. Видно, які вибірки стануть важкими після зростання даних, і що з цим робити заздалегідь.
- Опис індексів — під який запит створений кожен, з яких полів зібраний, яку вибірку покриває. Окремо позначені індекси, які прискорюють читання, але уповільнюють вставку.
- Розбір каскадів запитів — де застосунок замість однієї вибірки йшов у базу десятками звернень, і чим це замінено.
- Регламент обслуговування — періодичність перевірок, порядок міграцій схеми й правила відкату, якщо оновлення лягло невдало на частині пристроїв.
- Чек-лист перевірок перед релізом — вимірювання на реальних даних: час відкриття важкого екрана, кількість читань, розмір файлу бази.
- Правила роботи з фоновими завданнями — що допустимо під час переходу застосунку у фон, що відкладати й як не втратити операцію при згортанні.
- Інструкція для команди — куди дивитися при скарзі «гальмує», як зняти вимірювання продуктивності й що вважати нормою для екрана.
Комплект розрахований на розробника, який відкриє його вперше: мінімум теорії, конкретні дії та зрозумілі пороги.
Кейс: як прибрали підвисання на великих вибірках
До роботи над оптимізацією застосунок працював з офлайн-каталогом: розділ відкривався з локального сховища на SQLite, вибірка будувалася за кількома фільтрами й сортуванням.
На тестовому наборі з 500 записів відгук був миттєвим, на реальних даних — 40–60 тисяч позицій у розділі — відкриття займало 4–6 секунд, а синхронізація дописувала записи в ті самі транзакції, що й читання.
З чим прийшли
Розділ відкривався 4–6 секунд, на слабких пристроях — до 9. Частка звернень у підтримку з формулюванням «підвисає, не відкривається» трималася на 18% від усіх тікетів.
Окрема біда — збої при синхронізації: поки користувач читав список, фонове оновлення перетирало локальні правки, і він бачив застарілі дані.
Що змінили
Індекси зібрали під фактичні запити: один складений за ключами фільтра й сортування замість трьох одиночних. Прибрали N+1 — замість вибірки в циклі по кожній картці йде один пакетний запит.
Важкі читання винесли у фоновий контекст, запис синхронізації — в єдину послідовну чергу, тому читання й запис більше не перетинаються на одній транзакції. Список перевели на посторінкове довантаження по 50 позицій.
Відкриття розділу тепер 0,4–0,7 секунди, частка скарг на підвисання впала з 18% до 3%, а клас збоїв «синхронізація затерла локальні дані» закрився повністю: за три місяці спостереження — жодного випадку.
Чим відрізняється індексування від перебудови схеми зберігання?
Індексування змінює лише спосіб доступу до даних: склад таблиць, типи полів і зв'язки залишаються незмінними. Перебудова схеми змінює модель зберігання, тому без перенесення даних і версіонування не обходиться.
| Симптом | Імовірна причина | Що перевіряємо і змінюємо |
|---|---|---|
| Список відкривається довго при невеликому обсязі | Повне сканування на фільтрі й сортуванні | Складений індекс під порядок умов |
| Гальмує лише на великих обсягах | Немає покривного індексу, читаються зайві стовпці | Додаємо стовпці в індекс |
| На старті екрана — пачка однотипних запитів | Зв'язки читаються по одному запису | Попереднє завантаження зв'язків одним запитом |
| Інтерфейс підвисає при записі | Транзакції в основному потоці | Запис у фоновий контекст, розділення читання й запису |
Індекси закривають більшість випадків: якщо запит перестав сканувати таблицю цілком і йде за конкретними умовами, схему чіпати не потрібно. Індекс додається без перенесення даних і зберігає зворотну сумісність — старі версії застосунку читають ту саму базу.
Перебудова потрібна, коли вузьке місце в моделі, а не в доступі: дублі, відсутність розділення гарячих і архівних записів, зв'язки, що читаються по одному.
Тоді переносимо дані порціями з версіонуванням схеми й тримаємо обидві версії читабельними, поки оновлення не дійде до всіх пристроїв.
Вартість підтримки схеми визначається кількістю версій застосунку на пристроях, а не обсягом роботи. Кожна міграція має бути ідемпотентною й оборотною — інакше невдале оновлення на частині пристроїв невиправне.
Часті технічні сумніви перед стартом робіт
Список, що гальмує, на екрані не завжди означає проблему в інтерфейсі. Якщо прокручування провалюється в міру зростання даних, а на порожній базі екран відгукується миттєво — вузьке місце в шарі даних.
Якщо затримка однакова за будь-якого обсягу, справа у відмальовуванні, і переписаний шар даних приросту не дасть.
Оптимізація бази даних мобільного застосунку рідко вимагає переписувати всі запити відразу. Починаємо з планів виконання: частину навантаження знімають індекси під конкретні екрани, розбиття видачі на сторінки й перенесення важких операцій у фоновий потік.
Чи потрібен повний рефакторинг шару даних?
Повний — майже ніколи. Правимо вибірки, які йдуть без індексу або викликаються на кожен рядок списку. Робочі запити не чіпаємо: переписування додає ризик без вимірюваного ефекту.
Що робити з уже накопиченими обсягами?
Переносимо фоновою міграцією посторінково, з точкою відновлення й повтором при обриві. Застосунок продовжує працювати на поточних даних — «напівмігрованого» стану не виникає.
Чи не відваляться старі пристрої?
Структура таблиць та індекси сумісні з тими самими версіями системи, які підтримує застосунок. Нові механізми вмикаємо з перевіркою версії та запасним шляхом.
Наскільки безпечні правки й скільки часу займуть?
Міграції оборотні: знімок до зміни, скрипт відкату, прогін на копії реальних даних. Перший приріст за часом відгуку видно після першої ітерації, далі йдемо за пріоритетом екранів.
Обговоримо задачу й повернемося з оцінкою та планом
Щоб запропонувати реалістичну оцінку з оптимізації бази даних мобільного застосунку, а не орієнтир «на око», нам потрібен контекст: схема даних, важкі екрани й те, як застосунок поводиться на слабких пристроях і за поганого зв'язку. Нижче — що запитуємо на вході й що повертаємо у відповідь.
Що потрібно з вашого боку для старту:
- Вихідні коди з поточною гілкою — з правом на читання, без передачі ключів підпису й бойових секретів.
- Схема бази й міграції: версія схеми, склад індексів, обсяг таблиць і розподіл записів.
- 3-5 найповільніших сценаріїв: екран, дія користувача й що саме гальмує — відкриття списку, пошук, синхронізація.
- Тестовий стенд або знеособлений набір даних, на якому проблема відтворюється стабільно.
- Доступ до засобів вимірювань: журнали, звіти про помилки, якщо є — трасування запитів.
- Контакт інженера, який знає модель даних і історію рішень щодо шару зберігання.
Що повертаємо після розбору:
- Оцінку: перелік вузьких місць з очікуваним ефектом від кожного — у скільки разів скоротиться час запиту або розмір вибірки.
- Склад робіт: які індекси додаємо або перебудовуємо, де розбираємо множинні довантаження пов'язаних сутностей, що переносимо у фоновий контекст.
- План: порядок змін з перевірюваними віхами й точкою відкату на кожному кроці.
Опишіть задачу — повернемося з оцінкою та планом і узгодимо дату старту проєкту.







