Что такое SQLite: архитектура, применение и сравнение в 2026 году

Обновлено:

SQLite — самая распространённая база данных в мире. Она есть в Android и iOS, в браузерах Chrome и Firefox, в Windows и macOS, в стандартной библиотеке Python. По оценке разработчиков SQLite, в активном использовании находится больше триллиона баз SQLite.

При этом SQLite не похожа на привычные СУБД: у неё нет сервера, сетевых подключений, пользователей и паролей. Вся база — один файл на диске.

Архитектура SQLite: почему один файл

PostgreSQL, MySQL и MongoDB работают по клиент-серверной схеме: отдельный процесс слушает соединения, получает запросы по сети и возвращает результаты. Сервер нужно настраивать, он потребляет память даже в простое, а каждый запрос проходит через сеть или сокет.

SQLite — это библиотека, которая встраивается прямо в приложение. Вызов sqlite3.connect('database.db') открывает файл и работает с ним через системные вызовы ОС, без сетевого взаимодействия.

Традиционная СУБД:          SQLite:
┌──────────┐               ┌────────────────────────┐
│   App    │──TCP──►DB     │   App + SQLite lib     │
│          │◄──TCP──Server │                        │
└──────────┘               │   ┌──────────────────┐ │
                           │   │   database.db    │ │
                           │   └──────────────────┘ │
                           └────────────────────────┘

Что даёт встроенная архитектура:

  • нет сетевой задержки — запрос выполняется в процессе приложения;
  • нет отдельного сервиса, который надо поддерживать;
  • базу можно перенести или заархивировать как обычный файл (но копировать «живую» базу нужно правильно — см. раздел про бэкап);
  • не нужны права администратора;
  • нулевая конфигурация.

SQLite поддерживает полноценный SQL

Распространённое заблуждение — «SQLite не настоящая база». На деле она реализует большую часть SQL и многие расширения:

-- ACID-транзакции
BEGIN TRANSACTION;
INSERT INTO orders (user_id, total) VALUES (42, 1500.00);
INSERT INTO order_items (order_id, product_id, qty) VALUES (last_insert_rowid(), 7, 2);
COMMIT;

-- Оконные функции (с SQLite 3.25.0)
SELECT
    name,
    salary,
    RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_in_dept
FROM employees;

-- Рекурсивные CTE
WITH RECURSIVE categories AS (
    SELECT id, name, parent_id FROM category WHERE id = 1
    UNION ALL
    SELECT c.id, c.name, c.parent_id
    FROM category c
    JOIN categories p ON c.parent_id = p.id
)
SELECT * FROM categories;

-- JSON-функции (встроены по умолчанию с SQLite 3.38.0)
SELECT json_extract(metadata, '$.tags[0]') AS first_tag
FROM articles
WHERE json_extract(metadata, '$.published') = 1;

Чего в SQLite нет или что ограничено: хранимых процедур, полноценного ALTER TABLE (добавление, переименование и с версии 3.35 удаление колонки есть, а смену типа и ограничений — нет), строгой типизации по умолчанию (есть STRICT-таблицы с 3.37). RIGHT JOIN и FULL OUTER JOIN поддерживаются начиная с 3.39 — на старых версиях их нет.

WAL-режим: как SQLite справляется с конкурентностью

В SQLite в каждый момент пишет только одно соединение. В режиме журнала по умолчанию запись ещё и блокирует читателей. Режим WAL (Write-Ahead Logging) это смягчает:

-- Включить WAL (настройка сохраняется в файле базы)
PRAGMA journal_mode=WAL;

-- Типичные настройки для веб-приложения
PRAGMA synchronous=NORMAL;    -- меньше fsync; в WAL-режиме база не портится при сбое
PRAGMA cache_size=-64000;     -- около 64 МБ кэша страниц
PRAGMA foreign_keys=ON;       -- внешние ключи по умолчанию выключены
PRAGMA busy_timeout=5000;     -- ждать до 5 секунд, если база занята

В WAL-режиме:

  • читатели работают параллельно с писателем и не блокируют его;
  • писатель по-прежнему один в каждый момент времени;
  • запись обычно быстрее, чем в режиме rollback journal.

Пропускная способность зависит от диска, размера транзакций и настроек, поэтому меряйте на своей нагрузке. Для большинства веб-приложений с умеренной записью одного писателя хватает с запасом.

Важное следствие для бэкапа: в WAL-режиме свежие транзакции какое-то время живут в файле database.db-wal. Копия одного database.db через cp может оказаться неполной или несогласованной.

Ренессанс SQLite

Долгое время SQLite считали «не для продакшна». Сейчас вокруг неё появилось несколько инструментов, закрывающих исторические ограничения:

Turso / libSQL — открытый форк SQLite с репликами, встроенными (embedded) репликами, HTTP-доступом и векторным поиском; Turso Cloud — managed-сервис поверх него. Подробнее — в статье что такое Turso.

Cloudflare D1 — managed SQLite для Cloudflare Workers: доступ через binding, без TCP-подключения.

LiteFS — открытый проект Fly.io для репликации SQLite через FUSE: один писатель и read-реплики в других регионах.

PocketBase — готовый бэкенд на SQLite: база, авторизация, файлы и API в одном бинарнике. См. что такое PocketBase.

Litestream — непрерывная репликация WAL в S3: позволяет восстановить базу почти на любой момент времени.

# Litestream: непрерывная репликация SQLite в S3
litestream replicate /data/database.db s3://my-bucket/db

Когда SQLite — правильный выбор

Мобильные приложения. SQLite встроена в Android (с ней работает Room) и iOS (стандартное хранилище Core Data). Для локального хранения на устройстве это стандарт.

Десктопные приложения. Electron, Tauri, Qt: настройки, кэш и пользовательские данные без установки СУБД.

Один сервер и предсказуемая нагрузка. Если проект живёт на одном VPS и в основном читает данные, SQLite избавляет от отдельного сервера БД и сетевых задержек.

PocketBase и аналоги. SQLite встроена, отдельную БД обслуживать не нужно.

Edge. Cloudflare D1 и Turso дают SQLite рядом с пользователем.

Тесты. База в памяти (:memory:) создаётся практически мгновенно — удобно для тестов против настоящей СУБД.

Распространение данных. SQLite-файл — удобный формат для передачи наборов данных вместо CSV: сразу с SQL-запросами.

Когда SQLite — неправильный выбор

  • Несколько серверов должны писать в одну базу.
  • Много конкурентной записи: все записи выстраиваются в очередь к одному писателю.
  • Очень большие базы с тяжёлой аналитикой — технический предел около 281 ТБ, но обслуживать такой файл (бэкап, VACUUM, перенос) неудобно задолго до этого предела.
  • Нужны специфические возможности PostgreSQL: PostGIS, pgvector, row-level security, богатые типы.
  • База должна быть доступна по сети нескольким приложениям или людям напрямую.

Сравнение SQLite с PostgreSQL и MySQL

| Параметр | SQLite | PostgreSQL | MySQL | |---|---|---|---| | Тип | Встроенная | Клиент-серверная | Клиент-серверная | | Настройка | Нулевая | Умеренная | Умеренная | | Конкурентная запись | Один писатель (WAL) | Высокая (MVCC) | Высокая (InnoDB) | | Транзакции | ACID | ACID | ACID (InnoDB) | | Масштабирование | Вертикальное | Вертикальное + реплики | Вертикальное + реплики | | JSON | JSON-функции, JSONB с 3.45 | JSONB с индексами | Тип JSON и функции | | Полнотекстовый поиск | FTS5 | Встроенный, со словарями | Встроенный | | Геоданные | Расширение SpatiaLite | PostGIS | Встроенные spatial-типы | | Хранение | Один файл | Директория данных | Директория данных | | Типизация | Type affinity, STRICT-таблицы | Строгая | Строгая | | Типичное применение | Embedded, edge, MVP, один сервер | Веб, SaaS, аналитика | Веб, CMS |

Подробное сравнение с NoSQL-вариантами — в статье SQLite, PostgreSQL, MySQL или NoSQL.

SQLite в продакшне: практические настройки

# Python
import sqlite3

def get_db_connection(db_path: str) -> sqlite3.Connection:
    conn = sqlite3.connect(db_path, check_same_thread=False)
    conn.row_factory = sqlite3.Row  # доступ к колонкам по имени

    conn.execute("PRAGMA journal_mode=WAL")
    conn.execute("PRAGMA synchronous=NORMAL")
    conn.execute("PRAGMA foreign_keys=ON")
    conn.execute("PRAGMA cache_size=-32000")   # около 32 МБ
    conn.execute("PRAGMA busy_timeout=10000")  # 10 секунд
    conn.execute("PRAGMA temp_store=MEMORY")

    return conn
// Node.js: better-sqlite3 (в Bun — import { Database } from 'bun:sqlite')
import Database from 'better-sqlite3'

const db = new Database('production.db')

db.pragma('journal_mode = WAL')
db.pragma('synchronous = NORMAL')
db.pragma('foreign_keys = ON')
db.pragma('busy_timeout = 10000')
db.pragma('cache_size = -32000')

// Подготовленные выражения защищают от SQL-инъекций
const getUser = db.prepare('SELECT * FROM users WHERE id = ?')
const user = getUser.get(userId)

Бэкап SQLite: копия должна быть согласованной

Копирование файла (cp database.db backup.db) во время записи может дать повреждённую или неполную копию. Безопасные варианты:

# 1. Online backup API через CLI — безопасно при активной записи
sqlite3 database.db ".backup 'backup.db'"

# 2. VACUUM INTO (SQLite 3.27+): компактная копия, целевой файл не должен существовать
sqlite3 database.db "VACUUM INTO 'backup.db'"

# 3. Проверка копии
sqlite3 backup.db "PRAGMA integrity_check;"

Подробно, с cron и выгрузкой в S3, — в статье как сделать бэкап SQLite без повреждения. Чем отличаются непрерывная репликация и периодические копии — в сравнении Litestream vs dbsend.

Если нужны история версий и оповещения о пропущенных бэкапах, подойдёт бэкап SQLite в dbsend. CLI сам делает согласованную копию через VACUUM INTO, поэтому загружать можно рабочий файл:

dbsend backup ./database.db -d "$DBSEND_DATABASE_ID" -e sqlite -l "sqlite-$(date +%F-%H%M)"

При загрузке файл проверяется PRAGMA quick_check, а при dbsend restore — PRAGMA integrity_check и сверкой SHA-256.

Частые вопросы

Можно ли использовать SQLite в продакшне?

Да. Rails 8 предлагает SQLite как полноценный вариант для продакшна, PocketBase целиком построен на SQLite, Cloudflare D1 — managed SQLite. Условия: один основной сервер, включённый WAL-режим, busy_timeout и автоматические бэкапы с регулярной проверкой восстановления.

Насколько большой может быть база SQLite?

Технический предел — около 281 ТБ. На практике ограничивает не формат, а обслуживание: бэкап, VACUUM и перенос большого файла занимают время, а тяжёлые аналитические запросы в SQLite медленнее, чем в специализированных системах. Если база растёт быстро и запись конкурентная, планируйте переход на PostgreSQL заранее.

SQLite потокобезопасна?

В стандартных сборках (SQLITE_THREADSAFE=1, режим serialized) SQLite можно использовать из нескольких потоков. На одном соединении запросы всё равно выполняются по очереди, поэтому обычно каждому потоку дают своё соединение. WAL-режим позволяет читателям работать параллельно с писателем.

Чем SQLite отличается от Turso?

Turso строится на libSQL — открытом форке SQLite с репликами, встроенными репликами, HTTP-доступом и векторным поиском. Turso Cloud — managed-сервис поверх него. Базы libSQL используют формат файлов SQLite, но новые возможности форка обычным sqlite3 недоступны.

Есть ли у SQLite полнотекстовый поиск?

Да, расширение FTS5:

CREATE VIRTUAL TABLE articles_fts USING fts5(title, content);
SELECT * FROM articles_fts WHERE articles_fts MATCH 'база данных';

Стемминга для русского языка из коробки нет, поэтому поиск по словоформам слабее, чем в PostgreSQL с конфигурацией russian. Для серьёзного поиска по русскому тексту смотрите на Meilisearch или PostgreSQL.

Как мигрировать схему в SQLite?

Простые операции делает ALTER TABLE: добавить, переименовать и (с 3.35) удалить колонку. Для смены типа или ограничений создайте новую таблицу с нужной схемой, перенесите данные и переименуйте:

BEGIN;
CREATE TABLE users_new (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE);
INSERT INTO users_new (id, name, email) SELECT id, name, email FROM users;
DROP TABLE users;
ALTER TABLE users_new RENAME TO users;
COMMIT;

CREATE TABLE ... AS SELECT для этого не подходит: он теряет первичные ключи и ограничения. Индексы и триггеры пересоздайте после переноса. Автоматизировать миграции помогают drizzle-kit, Prisma Migrate или Alembic в batch-режиме. Перед миграцией сделайте бэкап.