SQLite — вбудована реляційна база даних, яка працює без окремого серверного процесу та зберігає всю базу в одному файлі на пристрої. Завдяки нульовій конфігурації, малому розміру бібліотеки та повній підтримці SQL, SQLite став стандартом для локального зберігання даних у мобільних застосунках. За даними SQLite Consortium (2025), ця СУБД використовується більш ніж у 4 мільярдах пристроїв, включаючи кожен смартфон на iOS та Android.
Головне
SQLite — це бібліотека на мові C, що реалізує реляційну СУБД без виділеного сервера. Вона вбудовується безпосередньо в застосунок, читає та записує дані у звичайний файл на файловій системі пристрою. Розмір бібліотеки становить близько 600 КБ, що робить SQLite найлегшою повнофункціональною SQL-базою даних.
SQLite підтримує більшу частину стандарту SQL:1999, включаючи JOIN, підзапити, тригери, представлення, індекси та віконні функції. Обмеження стосуються ALTER TABLE (обмежена підтримка) та повноцінних RIGHT/FULL OUTER JOIN. Тим не менш, для мобільних застосунків функціональності SQLite достатньо в 99% випадків локального зберігання.
За даними опитування розробників від Stack Overflow (2025), SQLite є найпопулярнішою базою даних для вбудованих рішень і займає третє місце за популярністю серед усіх СУБД після MySQL та PostgreSQL. У мобільній розробці SQLite використовується в кожному застосунку — безпосередньо або через обгортки.
Zero-configuration — SQLite не вимагає встановлення, налаштування прав, створення користувачів або запуску сервісу. Бібліотека підключається до проекту, і база даних створюється викликом однієї функції. Це радикально спрощує розгортання порівняно з клієнт-серверними СУБД, де потрібне встановлення сервера, налаштування портів та конфігурація користувачів.
Файл бази даних SQLite — звичайний крос-платформний файл, який можна скопіювати, проаналізувати, відправити по мережі або відновити з резервної копії. Формат файлу стабільний на рівні API: файли SQLite 3, створені в 2004 році, відкриваються поточною версією бібліотеки, що гарантує довгострокову сумісність даних.
Архітектура SQLite складається з восьми віртуальних машин: Tokenizer, Parser, Code Generator, VM, B-Tree, Pager, OS Interface та Utilities. SQL-запит проходить через Tokenizer (розбивка на токени), Parser (побудова AST), Code Generator (перетворення в байт-код) і виконується на віртуальній машині, яка читає сторінки даних через B-Tree та Pager.
SQLite використовує B-Tree для зберігання таблиць та індексів. Кожна таблиця зберігається як окреме B-Tree, де листові вузли містять рядки даних. Індекси також зберігаються як B-Tree, але з ключами в листях. Pager керує завантаженням сторінок (за замовчуванням 4096 байт) з файлу в пам'ять, забезпечуючи ACID-транзакції через журнал або WAL.
WAL (Write-Ahead Logging) — рекомендований режим для мобільних застосунків. Зміни спочатку записуються в окремий WAL-файл, а потім періодично переносяться в основну базу. WAL дозволяє одночасно читати з БД (старі дані) та писати в неї (через WAL), що підвищує продуктивність багатопотокових застосунків. Стандартний журнал (rollback journal) блокує читання при записі.
| Параметр | Rollback Journal | WAL (Write-Ahead Logging) |
|---|---|---|
| Читання при записі | Блокується | Дозволено (читає старі дані) |
| Продуктивність запису | Середня | Висока (послідовний запис у WAL) |
| Споживання диска | Менше (тільки журнал відкату) | Більше (WAL + основна БД) |
| Відновлення при збої | Відкат до останнього чекпоїнта | Відновлення з WAL (дані не втрачаються) |
| Рекомендація | Для однопотокових сценаріїв | Для типових мобільних застосунків |
Перемикання між режимами виконується одним SQL-запитом: PRAGMA journal_mode=WAL. Для мобільних застосунків з фоновою синхронізацією та UI-потоком, що одночасно читає дані, WAL забезпечує кращу продуктивність та відсутність блокувань інтерфейсу.
SQLite — не єдина опція для локального зберігання даних, але найбільш універсальна. Realm пропонує вищу швидкість прямого доступу до об'єктів у пам'яті, але використовує власний NoSQL-формат і має більший розмір бібліотеки. Core Data на iOS — це ORM-шар поверх SQLite, який додає керування графом об'єктів та скасування операцій.
Для більшості застосунків SQLite залишається оптимальним вибором завдяки передбачуваній продуктивності, нульовому vendor lock-in та перевіреній часом стабільності. Realm і Core Data виправдані в проектах зі складними об'єктними графами, реактивними запитами або вимогою до синхронізації між пристроями.
| Характеристика | SQLite | Realm | Core Data |
|---|---|---|---|
| Тип БД | Реляційна (SQL) | NoSQL (об'єктна) | ORM (поверх SQLite) |
| Розмір бібліотеки | ~600 КБ | ~4 МБ | Вбудовано в SDK Apple |
| Продуктивність | Середня | Висока (об'єкти в пам'яті) | Середня (накладні витрати ORM) |
| Платформи | iOS, Android, Web, Desktop | iOS, Android, Node.js | iOS, macOS |
| Vendor lock-in | Немає (відкритий стандарт) | Середній (власний формат) | Високий (тільки Apple) |
Вибір між SQLite, Realm і Core Data залежить від платформи, вимог до об'єктної моделі та стратегії синхронізації. Для крос-платформних проектів (KMP, Flutter) SQLite залишається єдиним універсальним вибором, що працює на всіх цільових платформах без змін у моделі даних.
Room — бібліотека з Android Jetpack, що надає ORM-шар поверх SQLite. Room автоматично генерує SQL-запити з анотованих DAO-інтерфейсів, перевіряє коректність запитів на етапі компіляції та підтримує міграції бази даних при зміні схеми. Room — рекомендований спосіб роботи з SQLite на Android.
SQLiteOpenHelper — низькорівневий API для прямого керування SQLite без ORM. Клас керує створенням, відкриттям та оновленням бази даних. SQLiteOpenHelper підходить для проектів з простими SQL-запитами або коли потрібен повний контроль над SQL-логікою без абстракції Room.
Сутність в Room анотується @Entity, а DAO — @Dao. Room транслює анотовані методи в SQL-запити: @Insert генерує INSERT, @Query — SELECT з вказаним SQL. Міграції додаються через Migration із зазначенням старої та нової версії схеми. Room перевіряє SQL на етапі компіляції, що виключає синтаксичні помилки в production.
@Entity
data class User(
@PrimaryKey val id: Long,
val name: String,
@ColumnInfo(name = "created_at")
val createdAt: Long
)
@Dao
interface UserDao {
@Query("SELECT * FROM user ORDER BY name ASC")
suspend fun getAllUsers(): List<User>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)
@Query("DELETE FROM user WHERE id = :id")
suspend fun deleteUser(id: Long)
}
Room автоматично генерує імплементацію UserDao_Impl, яка містить runtime-запити до SQLite через внутрішній RoomDatabase. Завдяки корутинам (suspend) DAO-методи виконуються асинхронно на фоновому потоці, не блокуючи UI. Flow-повертальні типи в @Query автоматично оновлюють результат при зміні таблиці.
FMDB — Objective-C обгортка над SQLite C API, історично перша популярна бібліотека для iOS. Надає об'єкти FMDatabase та FMResultSet для виконання запитів і отримання результатів. FMDB проста та мінімалістична, але не підтримує Swift-специфічні конструкції — опціонали, Codable, async/await.
GRDB — сучасна Swift-бібліотека для роботи з SQLite. Надає type-safe API, підтримку Codable, Combine Publishers, async/await, міграції та спостереження за змінами в реальному часі. GRDB є кращою для нових проектів на Swift завдяки повній інтеграції зі Swift Concurrency та кращій читабельності коду.
GRDB визначає таблиці через Record-класи, що відповідають протоколам FetchableRecord та TableRecord. Запити пишуться на Swift з type-safe синтаксисом, а не сирим SQL. GRDB також підтримує DatabaseMigrator для версіонування схеми та міграцій між версіями застосунку.
struct User: Codable, FetchableRecord, TableRecord {
var id: Int64
var name: String
var createdAt: Date
}
let dbPool = try DatabasePool(path: dbPath)
var migrator = DatabaseMigrator()
migrator.registerMigration("v1") { db in
try db.create(table: "user") { t in
t.autoIncrementedPrimaryKey("id")
t.column("name", .text).notNull()
t.column("createdAt", .datetime).notNull()
}
}
let users = try await dbPool.read { db in
try User.order(Column("name")).fetchAll(db)
}
DatabasePool використовує WAL-режим SQLite для конкурентного читання. Багато читачів можуть одночасно звертатися до бази, поки один писатель оновлює дані через WAL. GRDB автоматично керує з'єднаннями та транзакціями, забезпечуючи thread-safe доступ до бази з будь-якого потоку без ручної синхронізації.
Індекси — найефективніший спосіб прискорення SQLite-запитів. Індекс створюється на колонках, що беруть участь у WHERE, JOIN та ORDER BY. Для таблиці з 100000 записів пошук за індексованою колонкою виконується за мілісекунди замість секунд. Однак індекси сповільнюють INSERT та UPDATE, тому їх кількість має бути збалансована з частотою запису.
Пакетна вставка (batch insert) в рамках однієї транзакції радикально прискорює масове завантаження даних. Вставка 1000 записів по одній дає накладні витрати ~1 секунду. Ті ж 1000 записів в одній транзакції — ~5-10 мілісекунд. Різниця пояснюється тим, що кожна окрема INSERT створює нову транзакцію з синхронним записом на диск.
PRAGMA — SQLite-команди для налаштування поведінки бібліотеки. Ключові оптимізаційні прагми: PRAGMA synchronous=NORMAL (знижує частоту fsync), PRAGMA cache_size=-8000 (виділяє 8 МБ кешу), PRAGMA temp_store=MEMORY (тимчасові таблиці в пам'яті). Для мобільних застосунків з великими обсягами даних комбінація цих прагм прискорює запити в 2-3 рази.
Ще одна важлива оптимізація — попередня компіляція SQL-запитів (prepared statements). Якщо запит виконується багаторазово (наприклад, вставка 10000 рядків), компіляція SQL один раз з подальшим використанням statement знижує завантаження CPU на 30-50%. Room і GRDB автоматично кешують prepared statements, але при прямому використанні SQLite C API компіляцію потрібно виконувати вручну.
class UserRepository(private val db: RoomDatabase) {
suspend fun insertBatch(users: List<User>) {
db.withTransaction {
users.chunked(500).forEach { batch ->
batch.forEach { user ->
insertUser(user)
}
}
}
}
}
Пакетна вставка з withTransaction гарантує, що всі INSERT виконуються в рамках однієї транзакції. Розбивка на підбатчі (chunked) запобігає надто великому розміру однієї транзакції, яка могла б заблокувати інші потоки на тривалий час. Для фонової синхронізації розмір підбатча 500 записів дає оптимальний баланс швидкості та чуйності UI.
Часто задавані питання
Так, SQLite підтримує багатопотоковий доступ у режимі WAL. Кілька потоків можуть одночасно читати дані, але писати може тільки один. Room і GRDB керують синхронізацією автоматично. У режимі rollback journal (за замовчуванням) база повністю блокується при будь-якому записі.
Обмеження SQLite — 281 ТБ (теоретичний максимум). На практиці розмір бази обмежений доступною пам'яттю пристрою. Для мобільних застосунків комфортний розмір — до 1-2 ГБ. Бази більше 2 ГБ сповільнюють резервне копіювання, оновлення через App Store та збільшують споживання оперативної пам'яті.
SQLite не шифрує дані за замовчуванням — будь-який процес з доступом до файлу може їх прочитати. Для шифрування використовуйте SQLCipher (розширення з AES-256), Room з EncryptedDatabase (Android) або Encrypted Core Data на iOS. Шифрування додає 5-15% накладних витрат на читання та запис даних.
SQLite — вбудована (embedded) бібліотека, що не потребує серверного процесу. MySQL — клієнт-серверна СУБД з окремим сервером, користувачами, правами доступу та мережевим протоколом. SQLite зберігає базу в одному файлі, MySQL — у кількох файлах, керованих сервером. SQLite простіший і легший, MySQL потужніший і масштабованіший.
Для міграції SQLite використовуйте ALTER TABLE (додавання колонок) або створення нової таблиці з перенесенням даних та видаленням старої. Room автоматизує цей процес через класи Migration: вкажіть startVersion, endVersion та SQL-запити для зміни схеми. GRDB і FMDB надають аналогічні DatabaseMigrator.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також