SQLite или PostgreSQL: какую базу выбрать
Чем SQLite и PostgreSQL отличаются: четырнадцать критериев, какая база под какой проект, признаки, что SQLite уже мало, переносимый SQL, переезд по шагам и частые ошибки.
Коротко
SQLite — база в одном файле рядом с приложением: нечего устанавливать и администрировать, очень быстрое чтение, и её хватает большинству сайтов, блогов, каталогов и внутренних инструментов на одном сервере. PostgreSQL — отдельный сервер баз данных: много одновременных записей, доступ по сети с нескольких серверов, пользователи и права, строгие типы и расширения вроде PostGIS и pgvector. Берите SQLite, когда проект живёт на одном сервере и записей умеренно; PostgreSQL — когда пишущих много, серверов несколько или данным нужны свои правила. Начать на SQLite и переехать позже — нормально, если SQL с первого дня переносимый.
Коротко: что выбрать
Вопрос не в том, какая база лучше, а в том, где живут данные и кто в них пишет. Если приложение работает на одном сервере и большинство запросов читают — страницы, каталог, статьи, настройки, — SQLite даёт тот же результат с меньшей эксплуатацией: ни сервера, ни подключений, а копия — это копия файла.
PostgreSQL нужен, когда файла становится мало: много пользователей пишут в один момент, приложение работает на нескольких серверах, аналитики подключаются по сети, разным людям нужны разные права или проект опирается на расширения — карты, векторный поиск, временные ряды.
- Один сервер, в основном чтение — SQLite
- Много пишущих или серверов — PostgreSQL
- Не уверены — SQLite с переносимым SQL
SQLite и PostgreSQL: подробное сравнение
Четырнадцать критериев рядом — от того, как хранятся данные, до копий и хостинга.
| Критерий | SQLite | PostgreSQL |
|---|---|---|
| Где живут данные | один файл рядом с кодом | отдельный сервер баз данных |
| Установка | нет — библиотека в языке | сервер, пользователи, настройки |
| Чтение | очень быстрое, без сети | быстрое, через подключение |
| Одновременная запись | один пишущий за раз | много, блокировки на строках |
| Несколько серверов приложения | нет, одна машина | да, по сети |
| Пользователи и права | только права на файл | роли, права вплоть до строк |
| Типы | мягкие по умолчанию, строгие с STRICT | строгие всегда |
| Изменение схемы | ограниченный ALTER TABLE | полное, внутри транзакции |
| JSON | функции и оператор ->> | jsonb с индексами |
| Полнотекстовый поиск | FTS5, без морфологии | встроенный, со словарями языков |
| Расширения | немного | PostGIS, pgvector, TimescaleDB и сотни других |
| Репликация | внешние инструменты: Litestream, LiteFS | встроенная |
| Резервные копии | копия файла через .backup | pg_dump, восстановление на любой момент |
| Хостинг | везде, где работает код | VPS или управляемый сервис |
Какая база под какой проект
Двенадцать типичных проектов с рекомендацией и причиной.
| Проект | Брать | Почему |
|---|---|---|
| Корпоративный сайт или блог | SQLite | почти одно чтение |
| Каталог или справочник | SQLite | быстрое чтение, копия — один файл |
| Внутренний инструмент для команды | SQLite | мало пишущих, нечего администрировать |
| Прототип или MVP | SQLite | старт сегодня, переезд, когда вырастет |
| Интернет-магазин с заказами | PostgreSQL | заказы и остатки меняются в один момент |
| SaaS с множеством клиентов | PostgreSQL | много пишущих, права вплоть до строк |
| CRM или учёт | PostgreSQL | строгие типы и постоянная запись |
| Приложение на нескольких серверах | PostgreSQL | один файл не делят по сети |
| Карты и геоданные | PostgreSQL | PostGIS |
| Поиск по смыслу, RAG | PostgreSQL | pgvector рядом с данными |
| Расширение браузера или программа | SQLite | база внутри приложения, без сервера |
| Аналитика по сети | PostgreSQL | аналитики подключаются своими инструментами |
Признаки, что SQLite уже мало
SQLite редко отказывает внезапно — она подаёт сигналы. Любой из них — повод планировать переезд.
-
01
«database is locked» в журналах
Ошибка появляется даже с WAL и
busy_timeout— пишущие слишком долго стоят в очереди. -
02
Второй сервер
Приложению нужно работать на двух машинах, и обе должны писать.
-
03
Доступ по сети
Аналитикам, отчётам или другому сервису нужно читать данные напрямую.
-
04
Разные права
Кто-то должен видеть только свои строки, и одного кода как защиты мало.
-
05
Нужно расширение
Карты, векторный поиск или временные ряды — PostGIS, pgvector, TimescaleDB.
-
06
Поиск с морфологией
Пользователи ищут по формам слова, а FTS5 находит только точные.
Разница в SQL: 3 примера
Что работает одинаково в обеих, где они расходятся и как переехать. Каждый пример выполнен в SQLite 3.40 и PostgreSQL 16.
Переносимый SQL
Таблица и upsert с RETURNING, которые выполняются без правок в обеих базах, — основа лёгкого переезда.
-- Один файл, две базы: выполняется без правок в SQLite 3.35+ и PostgreSQL
CREATE TABLE products (
sku TEXT PRIMARY KEY,
title TEXT NOT NULL,
price INTEGER NOT NULL CHECK (price >= 0),
stock INTEGER NOT NULL DEFAULT 0
);
-- Добавить товар, а если артикул уже есть — обновить цену и прибавить остаток
INSERT INTO products (sku, title, price, stock)
VALUES ('A-100', 'Дубовый стол', 24000, 3)
ON CONFLICT (sku) DO UPDATE
SET price = excluded.price,
stock = products.stock + excluded.stock
RETURNING sku, price, stock;
-- первый запуск: A-100 | 24000 | 3
-- второй запуск: A-100 | 24000 | 6
Типы: главное различие
Один INSERT — три результата. Обычная таблица SQLite хранит текст в числовой колонке, STRICT и PostgreSQL отказывают.
-- Тот же INSERT, но вместо числа слово
INSERT INTO products (sku, title, price)
VALUES ('C-300', 'Берёзовый стул', 'бесплатно');
-- SQLite, обычная таблица: строка сохранена, price = 'бесплатно'
-- (CHECK тоже проходит: в SQLite любой текст «больше» любого числа)
-- SQLite, таблица объявлена STRICT:
-- cannot store TEXT value in INTEGER column products.price
-- PostgreSQL:
-- invalid input syntax for type integer: "бесплатно"
Перенос таблицы
Выгрузка в CSV, загрузка через \copy и сдвиг счётчика id — шаг, который чаще всего забывают.
# Перенос таблицы из SQLite в PostgreSQL. Таблица в PostgreSQL создаётся заранее:
# CREATE TABLE products (id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
# sku text UNIQUE NOT NULL, title text NOT NULL, price integer NOT NULL);
# 1. Выгрузить из SQLite в CSV
sqlite3 -header -csv app.db "SELECT id, sku, title, price FROM products" > products.csv
# 2. Загрузить в PostgreSQL, сохранив те же id
psql "$DATABASE_URL" -c "\copy products (id, sku, title, price) FROM 'products.csv' CSV HEADER"
# 3. Сдвинуть счётчик id за загруженные строки,
# иначе следующий INSERT упадёт с «duplicate key value violates unique constraint»
psql "$DATABASE_URL" -c "SELECT setval(pg_get_serial_sequence('products', 'id'), max(id)) FROM products"
Переезд с SQLite на PostgreSQL: что учесть
Данные переезжают легко; сюрпризы — в том, что SQLite прощала, а PostgreSQL нет.
-
01
Сначала проверить типы
Найти текст в числовых колонках через
typeof()и вычистить до переезда, иначе загрузка встанет на первой плохой строке. -
02
pgloader для больших баз
Он читает файл SQLite напрямую и переносит схему и данные одной командой.
-
03
Счётчики id
После загрузки с явными id каждый счётчик сдвигают через
setval. -
04
Логические значения и даты
SQLite хранит их как 0/1 и текст — в PostgreSQL они становятся
booleanиtimestamptz. -
05
LIKE и регистр
В SQLite
LIKEне различает регистр латиницы, в PostgreSQL различает — поиску нуженILIKE. -
06
Двойные кавычки
SQLite часто принимает строку в двойных кавычках; PostgreSQL читает её как имя колонки и падает.
Частые ошибки при выборе между ними
-
PostgreSQL «на всякий случай»
Отдельный сервер, обновления и копии ради сайта, который только читает.
-
SQLite на сетевом диске
Блокировки по сети ненадёжны — файл портится.
-
Винить SQLite без WAL
Большинство жалоб «SQLite тормозит» исчезают после WAL и
busy_timeout. -
Годы на мягких типах
Без STRICT копятся грязные данные и всплывают в день переезда.
-
SQL, привязанный к одной базе
Особые функции по всему коду превращают простой переезд в переписывание.
-
Переезд ради названия
Если медленно из-за запросов и индексов, PostgreSQL тоже будет медленной.
Вопросы о SQLite и PostgreSQL
Подходит ли SQLite для рабочего проекта?
Да, для проекта на одном сервере с умеренной записью — с WAL, busy_timeout и регулярными копиями.
Какая быстрее?
SQLite — на чтении в одном приложении, потому что нет сети; PostgreSQL — когда много пишут одновременно.
Какой трафик выдержит SQLite?
Чтение редко становится пределом; предел — сколько записей приходит в один момент.
Можно начать на SQLite и переехать позже?
Да, если SQL переносимый, а таблицы STRICT, — тогда переезд занимает часы, а не недели.
Поддерживает ли SQLite JSON?
Да, функции и оператор ->>; PostgreSQL добавляет jsonb с индексами по полям.
Как делать копии каждой?
SQLite — .backup или Litestream, а не простое копирование живого файла; PostgreSQL — pg_dump и архив WAL.
А как же MySQL?
Это серверная база, как PostgreSQL; чем они отличаются — в отдельном сравнении.
Форма
Подобрать
базу
Работаю с обеими: SQLite — для проектов на одном сервере, PostgreSQL — когда нагрузке, команде или данным нужно больше. Расскажите о проекте — отвечу в течение рабочего дня.