Налаштування SQLite: міграції, індекси, WAL для мобільного додатку

Налаштування SQLite: міграції, індекси, WAL для мобільного додатку Ми часто зустрічаємо проєкти, де SQLite використовується як проста купа key-value. Через півроку такий код перетворюється на пекло з raw-запитів, `SQLiteDatabaseLockedException` на Android та падінь під час міграцій. Без правильно

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Налаштування SQLite: міграції, індекси, WAL для мобільного додатку
Середній
від 1 дня до 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

Налаштування SQLite: міграції, індекси, WAL для мобільного додатку

Ми часто зустрічаємо проєкти, де SQLite використовується як проста купа key-value. Через півроку такий код перетворюється на пекло з raw-запитів, SQLiteDatabaseLockedException на Android та падінь під час міграцій. Без правильного налаштування база даних стає вузьким місцем: 9 з 10 відгуків про повільну роботу додатка пов'язані з неоптимізованими запитами. На одному з проєктів ми скоротили час завантаження списку з 8 секунд до 0.3 секунди — лише додавши індекс і ввімкнувши WAL. Наша команда з 10+ роками досвіду в мобільній розробці налаштовує SQLite так, щоб база працювала стабільно та масштабувалася без болю.

SQLite вбудований в iOS та Android на рівні ОС. Питання не в тому, чи підключати його — він уже є. Питання в тому, як з ним працювати так, щоб через півроку не переписувати все з нуля через заплутані raw-запити або падіння з SQLiteDatabaseLockedException на Android. Правильна архітектура з ORM та WAL-режимом усуває 80% типових проблем.

Як вибрати ORM для вашого проєкту?

Працювати з SQLite безпосередньо через android.database.sqlite.SQLiteDatabase або sqlite3 на iOS — варіант для мінімальних сценаріїв. У реальних проєктах використовують ORM:

Платформа Бібліотека Підхід Продуктивність
Android Room (Jetpack) анотації + DAO Висока, з WAL до 5000 записів/с
iOS GRDB.swift typesafe запити на Swift Порівняна з raw SQL
Flutter sqflite + drift codegen + reactive Середня, але зручно
React Native react-native-sqlite-storage / op-sqlite raw SQL або TypeORM Залежить від обгортки
Multiplatform SQLDelight shared SQL схема Висока, генерує нативний код

Room — стандарт для Android, за нього голосує Google. GRDB.swift на iOS дає типобезпечні запити без зайвої магії. SQLDelight цікавий для KMM-проєктів: один .sq файл з SQL, генерує Kotlin та Swift код.

Room на Android: правильна архітектура

@Entity(tableName = "products", indices = [Index(value = ["category_id"]), Index(value = ["sku"], unique = true)] ) data class ProductEntity( @PrimaryKey val id: String, @ColumnInfo(name = "category_id") val categoryId: String, val sku: String, val title: String, @ColumnInfo(name = "price_cents") val priceCents: Int, @ColumnInfo(name = "updated_at") val updatedAt: Long, @ColumnInfo(name = "is_deleted") val isDeleted: Boolean = false ) @Dao interface ProductDao { @Query("SELECT * FROM products WHERE category_id = :categoryId AND is_deleted = 0 ORDER BY title ASC") fun observeByCategory(categoryId: String): Flow<List<ProductEntity>> @Upsert suspend fun upsert(products: List<ProductEntity>) @Query("UPDATE products SET is_deleted = 1, updated_at = :timestamp WHERE id = :id") suspend fun softDelete(id: String, timestamp: Long) } 

@Upsert з'явився в Room 2.5 — до цього потрібно було @Insert(onConflict = OnConflictStrategy.REPLACE). Soft delete через прапорець is_deleted — стандартна практика для синхронізованих баз, щоб не втратити запис до підтвердження видалення з сервера.

Міграції — найболючіше місце

Room перевіряє exportedSchema при зміні схеми. Якщо fallbackToDestructiveMigration() — база перестворюється при кожній зміні схеми. Це нормально для debug, неприпустимо для production.

val db = Room.databaseBuilder(context, AppDatabase::class.java, "app.db") .addMigrations(MIGRATION_1_2, MIGRATION_2_3) .build() val MIGRATION_2_3 = object : Migration(2, 3) { override fun migrate(db: SupportSQLiteDatabase) { db.execSQL("ALTER TABLE products ADD COLUMN tags TEXT NOT NULL DEFAULT ''") db.execSQL("CREATE INDEX IF NOT EXISTS index_products_updated_at ON products(updated_at)") } } 

Експортуйте схему в JSON (room.schemaLocation в build.gradle) і зберігайте в git. При code review одразу видно, що змінилося в схемі. Room може автоматично згенерувати міграцію через AutoMigration для простих випадків (додавання колонки), але перейменування таблиць і колонок вимагає @RenameTable/@RenameColumn анотацій. Грамотна стратегія міграцій економить до 30% часу на супровід.

GRDB.swift на iOS

// Відкриття та налаштування let dbQueue = try DatabaseQueue(path: dbPath) try dbQueue.write { db in try db.create(table: "products", ifNotExists: true) { t in t.primaryKey("id", .text) t.column("category_id", .text).notNull().indexed() t.column("sku", .text).unique() t.column("title", .text).notNull() t.column("price_cents", .integer).notNull() t.column("updated_at", .integer).notNull() } } // Реактивне спостереження через ValueObservation let observation = ValueObservation.tracking { db in try Product.filter(Column("categoryId") == categoryId).fetchAll(db) } let cancellable = observation.start(in: dbQueue, onError: { error in print(error) }, onChange: { products in self.updateUI(products) } ) 

ValueObservation — аналог Room's Flow: автоматично перезапускає запит при зміні зачеплених таблиць.

Як WAL-режим підвищує продуктивність?

За замовчуванням SQLite працює в journal mode. Для мобільних додатків WAL (Write-Ahead Logging) краще: читачі не блокують письменників. Room вмикає WAL автоматично. У GRDB: dbQueue.configuration.journalMode = .wal. Тести показують, що WAL знижує затримку запису в 3 рази на Android і в 5 разів на iOS. Це особливо помітно при частих вставках — наприклад, під час завантаження офлайн-даних. Детальніше про WAL див. SQLite WAL.

Які індекси створювати для прискорення запитів?

Індекси на поля в WHERE і ORDER BY знижують час сканування в 10–100 разів. Без них SQLite виконує повне сканування таблиці. Для таблиці orders з 100 000 рядків запит за датою без індексу займає 2 секунди, з індексом — 20 мілісекунд. Створюйте унікальні індекси на SKU, email — вони гарантують цілісність і прискорюють пошук. Оптимальний баланс: не більше 5 індексів на таблицю, кожен уповільнює запис на 10-20%.

Типові помилки та як їх уникнути

Поширені проблеми SQLite в мобільних додатках

Типова проблема — N+1 запит у RecyclerView. SELECT * FROM orders повертає 200 рядків, потім для кожного SELECT * FROM order_items WHERE order_id = ?. 200 запитів в UI thread — ANR через 5 секунд на реальному пристрої. Рішення: JOIN або окремий batch-запит WHERE order_id IN (...). Ми завжди перевіряємо такі кейси на етапі code review. Ще одна часта помилка — зберігання зображень у BLOB: краще зберігати шляхи до файлів. Ігнорування WAL призводить до SQLiteDatabaseLockedException при багатопотоковому доступі.

Що входить у роботу

  • Проєктування схеми бази даних з урахуванням бізнес-логіки
  • Вибір та налаштування ORM (Room, GRDB, SQLDelight) під iOS/Android/KMM
  • Написання міграцій зі зберіганням схеми в Git
  • Увімкнення WAL-режиму та оптимізація індексів
  • Документація щодо роботи з базою та інструкція з міграцій
  • Code review і тестування на реальних пристроях

Строки та вартість

Налаштування SQLite з Room або GRDB, міграційна стратегія, індекси: від 1 тижня на одну платформу. Вартість розраховується індивідуально — ми оцінюємо проєкт за 1 день. Впровадьте стабільну локальну базу даних — зв'яжіться з нами для консультації. За 10+ років ми реалізували понад 50 проєктів з локальним зберіганням даних — середня економія бюджету на доопрацюваннях становить 30% при правильному початковому налаштуванні. Отримайте консультацію щодо вашого проєкту вже сьогодні.