Чем отличаются альтернативы PostgreSQL и как обосновать выбор
MySQL создана в 1995 году шведской компанией MySQL AB. В 2008-м её купила Sun Microsystems, а в 2010-м вместе с Sun она перешла к Oracle — то есть к прямому конкуренту на рынке коммерческих СУБД.
Сообщество восприняло это как угрозу. Михаэль Видениус, один из основателей MySQL, создал форк — MariaDB, названный в честь его дочери. Форк остался свободным и развивается независимо.
Архитектурная особенность MySQL и MariaDB, которой нет у PostgreSQL: способ физического хранения выбирается для каждой таблицы отдельно.
| Движок | Транзакции | Блокировки | Применение |
|---|---|---|---|
InnoDB | да, ACID | на уровне строк | основной выбор по умолчанию |
MyISAM | нет | на уровне таблицы | устаревший, встречается в старых системах |
MEMORY | нет | таблица | временные данные в оперативной памяти |
Aria | нет | таблица | MariaDB, улучшенный MyISAM |
ColumnStore | ограниченно | — | MariaDB, аналитика |
MyISAM не поддерживает ни транзакций, ни внешних ключей: команда FOREIGN KEY принимается синтаксически и молча игнорируется. Целостность при этом никем не обеспечивается. Это одна из главных причин, по которой в старых PHP-проектах базы приходят в противоречивое состояние. При работе с унаследованной системой первым делом проверьте движок таблиц.В PostgreSQL сменных движков нет — есть одна реализация хранения на основе многоверсионности. Это означает меньше вариантов настройки и меньше способов выстрелить себе в ногу.
Стандарт SQL описывает ядро языка, но каждая СУБД расширяет и трактует его по-своему. Ниже — различия, на которые чаще всего наталкиваются при переносе кода.
| Что | PostgreSQL | MySQL / MariaDB |
|---|---|---|
| Автоинкремент | GENERATED ALWAYS AS IDENTITY, SEQUENCE | AUTO_INCREMENT, последовательностей нет* |
| Кавычки для имён | двойные: "user" | обратные: `user` |
| Регистр имён без кавычек | приводится к нижнему | зависит от файловой системы |
| Строковая конкатенация | || | concat(); || означает OR |
| Вставка или обновление | ON CONFLICT ... DO UPDATE | ON DUPLICATE KEY UPDATE |
| Транзакционный DDL | да | нет, неявная фиксация |
| Логический тип | boolean | tinyint(1) |
| Перечисление | CREATE TYPE ... AS ENUM | ENUM прямо в столбце, ещё есть SET |
| Массивы, диапазоны | есть | нет |
| JSON | jsonb с индексами GIN | json, индексация через генерируемые столбцы |
| Регистрозависимость строк | да, сравнение точное | зависит от collation, часто без учёта регистра |
| Полнотекстовый поиск | tsvector + GIN | FULLTEXT-индексы |
| Оконные функции, CTE | давно | с MySQL 8 / MariaDB 10.2 |
*В MariaDB последовательности появились в версии 10.3, в MySQL их по-прежнему нет.
-- PostgreSQL
CREATE TABLE users (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name varchar(100) NOT NULL,
email varchar(200) UNIQUE
);
INSERT INTO users (name) VALUES ('Анна')
ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name;
-- MySQL / MariaDB
CREATE TABLE users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(200) UNIQUE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO users (name) VALUES ('Анна')
ON DUPLICATE KEY UPDATE name = VALUES(name);
utf8 хранит не более трёх байт на символ и не вмещает эмодзи и часть редких символов. Настоящий UTF-8 называется utf8mb4 — его и нужно указывать всегда. Симптом ошибки: текст обрезается на первом эмодзи или запрос падает с Incorrect string value. В PostgreSQL такой проблемы нет: UTF8 означает UTF-8.0000-00-00 — вместо того чтобы отвергнуть операцию. Начиная с версии 5.7 строгий режим включён по умолчанию, но в унаследованных конфигурациях он часто отключён. PostgreSQL всегда отвергает некорректные данные — и это принципиальная разница в философии: лучше ошибка, чем испорченные данные.SQLite — не сервер. Это библиотека, работающая внутри процесса приложения; вся база лежит в одном файле. Установки и администрирования не требует.
Ноль настройки, база — один файл, легко копировать и передавать. Очень быстрая на чтение. Самая распространённая СУБД в мире: она есть в каждом телефоне, браузере и множестве настольных программ.
Одновременная запись только одна на всю базу. Нет пользователей и прав. Нет сетевого доступа. Динамическая типизация: тип — рекомендация, а не ограничение.
-- В SQLite это выполнится без ошибки:
CREATE TABLE t (age INTEGER);
INSERT INTO t VALUES ('не число'); -- строка попадёт в целочисленный столбец
-- В PostgreSQL — ошибка:
-- ERROR: invalid input syntax for type integer
CREATE TABLE ... STRICT), где типы проверяются. По умолчанию поведение осталось прежним ради совместимости. Уместное применение SQLite — мобильные и настольные приложения, локальный кэш, файлы обмена данными, прототипы. Неуместное — веб-приложение с несколькими одновременно пишущими пользователями.| Задача | Разумный выбор |
|---|---|
| Веб-приложение, новый проект | PostgreSQL |
| Информационная система для госзаказчика | Postgres Pro |
| Поддержка существующей CMS | MySQL или MariaDB — что уже стоит |
| Мобильное приложение, локальные данные | SQLite |
| Аналитика на сотни миллионов строк | ClickHouse рядом с основной базой |
| Кэш и сессии | Redis рядом с основной базой |
| Прототип на две недели | что угодно знакомое |
Перенос базы с одной СУБД на другую — не выгрузка и загрузка данных, а проект. Данные переносятся относительно легко; ломается всё остальное.
tinyint(1) ↔ boolean, ENUM, 0000-00-00 как дата в старых базах MySQL.AUTO_INCREMENT превращается в IDENTITY, счётчики требуют синхронизации (раздел 7).Users в MySQL и users в PostgreSQL — источник массовых правок в коде.LIMIT, ON DUPLICATE KEY, обратные кавычки, конкатенация.MyISAM, внешние ключи там не работали — и данные почти наверняка содержат «висячие» ссылки, которые PostgreSQL откажется принимать.pgloader для переноса из MySQL и SQLite, ora2pg для Oracle. Они переносят структуру и данные, но не бизнес-логику. Реалистичная оценка: данные — 10% работы, приложение — 90%. Обязательный этап, который чаще всего пропускают, — сверка: число строк по каждой таблице, контрольные суммы ключевых показателей, параллельная работа двух систем на боевых данных до переключения.MyISAM молча игнорирует внешние ключи и не поддерживает транзакцийutf8mb4, а не utf8