SQLite долго воспринимали лишь как встраиваемую базу для мобильных клиентов, IoT и локальной разработки. Считалось, что серьёзному продакшену нужен клиент-серверный PostgreSQL или MySQL. Этот взгляд устарел. Быстрые NVMe SSD и тренд на single-tenant edge-развёртывания превратили сетевой roundtrip в главный тормоз. Запустив SQLite прямо в процессе приложения на том же сервере, вы убираете сеть полностью. Чтения становятся memory-mapped операциями с задержкой меньше миллисекунды. Но чтобы раскрыть такой потенциал, настройками по умолчанию не обойтись — нужны WAL, управление блокировками, кэш и кастомные VFS.
По умолчанию SQLite использует rollback-журнал: перед записью страница копируется в отдельный файл, и writer блокирует reader’ов, а reader’ы — writer’а. Это лечится включением WAL: PRAGMA journal_mode = WAL. Теперь новые транзакции дописываются в файл .sqlite-wal, читатели спокойно читают основную базу и неизменённые страницы WAL, никто никого не блокирует. Но WAL-файл бесконечно растёт, поэтому нужен checkpointing — слияние страниц обратно в основную базу. Автоматический checkpoint может буксовать, если всегда есть активный читатель. Для производства лучше управлять им в фоне, вызывая PRAGMA wal_checkpoint(PASSIVE) по расписанию. Дополнительно снижаем накладные расходы на синхронизацию: PRAGMA synchronous = NORMAL. В WAL-режиме это безопасно — при крахе потеряются только незакоммиченные транзакции, целостность базы не пострадает.
WAL разрешает конкурентные чтения и запись, но писатель всё равно один. Второй writer сразу получит SQLITE_BUSY. Спасает busy-таймаут: PRAGMA busy_timeout = 5000 заставляет движок с экспоненциальной задержкой перепробовать захват блокировки. И строгое правило для пишущих транзакций — всегда начинать с BEGIN IMMEDIATE. Стандартный DEFERRED стартует без блокировок и при двух параллельных чтениях-с-последующей-записью легко приводит к дедлоку. IMMEDIATE сразу берёт зарезервированную блокировку и дедлоки исключает.
Кэш и memory-mapped I/O решают вопрос дисковых операций. Размер кэша по умолчанию смешной. Поднять его: PRAGMA cache_size = -64000 (около 64 МБ). Для чтений ещё радикальнее — PRAGMA mmap_size = 1073741824 (1 ГБ). Если база меньше, ядро ОС отображает весь файл в память, и чтения становятся простой арифметикой указателей.
Встроенный слой VFS — настоящая магия. SQLite не пишет в файловую систему напрямую, а делегирует операции своему VFS-модулю. Это позволило создать репликационные движки. Litestream перехватывает записи на уровне ОС и стримит инкрементальные WAL-фреймы в AWS S3 каждую секунду — point-in-time восстановление почти без оверхеда. LiteFS — это FUSE-базированная VFS, распределяющая SQLite-базу по кластеру и в реальном времени реплицирующая транзакции на read-реплики. В облачных средах с эфемерными дисками (AWS ECS, Kubernetes, Fly.io) такой VFS-инструмент обязателен для долговечности и высокой доступности.
Итоговый production-ready набор при инициализации каждого соединения: PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA busy_timeout = 5000; PRAGMA cache_size = -64000; PRAGMA mmap_size = 1073741824; PRAGMA foreign_keys = ON; PRAGMA journal_size_limit = 67108864; PRAGMA auto_vacuum = INCREMENTAL;. С такой конфигурацией один инстанс SQLite на скромном VPS спокойно переваривает сотни одновременных запросов и миллионы обращений в день. Если требуется распределённая запись по регионам или объём данных переваливает за несколько терабайт, правильным выбором остаётся PostgreSQL. Но если приложение read-heavy, укладывается в пару сотен гигабайт и требует минимальных задержек, SQLite, запущенный прямо на сервере приложения, даёт высокую производительность, простоту эксплуатации и отличную экономию.