25. PostgreSQL vs MySQL — архитектура (облегчённая)
🧭 Введение: зачем junior понимать архитектуру СУБД
Когда мы сравниваем PostgreSQL и MySQL, чаще всего обсуждают синтаксис запросов.
Но в реальной работе поведение системы определяется ещё и архитектурой: как СУБД хранит версии строк, какие есть движки хранения, как устроены автоинкрементные ключи и работа с JSON.
Но в реальной работе поведение системы определяется ещё и архитектурой: как СУБД хранит версии строк, какие есть движки хранения, как устроены автоинкрементные ключи и работа с JSON.
В этой теме разбираем архитектуру на прикладном уровне, без перегруза:
- философия PostgreSQL и MySQL;
- MVCC простыми словами;
- storage engines и роль InnoDB;
SERIALvsAUTO_INCREMENT;JSONvsJSONB.
💡 Совет:
Не учите архитектуру «ради теории». Привязывайте каждый пункт к вопросу: как это влияет на код и схему.
✅ Вывод:
Даже облегчённое понимание архитектуры резко снижает количество ошибок при проектировании и миграциях.
⚠️ Проблема -> решение
Типичная проблема:
- команда пишет SQL «по памяти», но не учитывает архитектурные различия СУБД;
- решения, которые хорошо работают в PostgreSQL, переносят в MySQL без адаптации (и наоборот);
- на проде появляются неожиданные эффекты: блокировки, разная работа JSON, разный подход к ключам.
Решение:
- знать базовую карту архитектурных отличий;
- при проектировании сразу выбирать диалектный паттерн (id, JSON, движок хранения);
- фиксировать в документации команды, почему выбран именно такой путь.
🟢 Если совсем просто:
Один и тот же SQL может вести себя по-разному, потому что «под капотом» PostgreSQL и MySQL устроены не одинаково.
🎯 Как понять, что этап прошёл успешно:
Вы можете объяснить ключевые отличия без сложных терминов и сразу показать прикладной выбор для проекта.
🛠️ Чем помогает и как работает
Архитектурное понимание нужно, чтобы писать предсказуемый код и не лечить проблемы уже после релиза.
Оно особенно важно в задачах миграции, multi-DB окружениях и системах с высокой нагрузкой на чтение/запись.
Оно особенно важно в задачах миграции, multi-DB окружениях и системах с высокой нагрузкой на чтение/запись.
🟢 Если совсем просто:
Вы заранее понимаете, какие решения «нативны» для PostgreSQL, а какие для MySQL.
🎯 Как понять, что этап прошёл успешно:
В архитектурных обсуждениях вы аргументируете решения через поведение СУБД, а не через «мне так привычнее».
Чем помогает:
- уменьшает риск неверного выбора типа данных;
- ускоряет code review схемы;
- делает миграции более предсказуемыми;
- помогает корректно выбирать индексы и формат хранения данных.
Как это работает:
- Шаг 1: фиксируем бизнес-задачу (транзакционность, тип нагрузки, интеграции);
- Шаг 2: выбираем архитектурные решения под целевую СУБД;
- Шаг 3: закладываем ограничения и формат данных в схему;
- Шаг 4: проверяем на реальных сценариях чтения/записи.
✅ Вывод:
Архитектура СУБД — это не отдельная «академическая тема», а часть ежедневного инженерного выбора.
📚 Ключевые термины (простыми словами)
Перед практикой синхронизируем словарь.
🟢 Если совсем просто:
Одинаковые термины в PostgreSQL и MySQL иногда означают разные механики.
🎯 Как понять, что этап прошёл успешно:
Вы уверенно объясняете эти слова на рабочем языке команды, без перегруза формулами.
- Философия СУБД — общий подход продукта: как база развивает возможности и какие компромиссы делает.
- MVCC (Multi-Version Concurrency Control) — хранение версий строк, чтобы чтение и запись меньше блокировали друг друга.
- Storage Engine — подсистема хранения/транзакций в MySQL (например, InnoDB).
- SERIAL — удобная обёртка в PostgreSQL для автоинкрементного числового id.
- AUTO_INCREMENT — автоувеличение числового id в MySQL.
- JSONB — бинарный формат JSON в PostgreSQL с лучшей поддержкой индексов и операций.
✅ Вывод:
Термины дают общую инженерную рамку: как мыслить про поведение СУБД, а не только про синтаксис.
🧩 1. Философия PostgreSQL и MySQL
PostgreSQL обычно воспринимают как систему с акцентом на расширяемость, строгие возможности SQL и богатый функционал «из коробки».
MySQL часто выбирают за простоту старта, широкую распространённость и практичный фокус на веб-нагрузках.
MySQL часто выбирают за простоту старта, широкую распространённость и практичный фокус на веб-нагрузках.
🟢 Если совсем просто:
PostgreSQL чаще про «богатый toolkit», MySQL — про «простой и понятный путь для типовых задач».
🎯 Как понять, что этап прошёл успешно:
Вы не спорите «что лучше», а связываете выбор с конкретной задачей проекта.
Назначение:
Понять, почему в двух СУБД разные «дефолтные» решения считаются нормой.
Простыми словами:
Философия влияет на то, какие паттерны команда использует чаще всего.
Для новичка:
Нельзя переносить архитектурные привычки 1-в-1 без проверки на целевой СУБД.
Аналогия:
Две машины могут ехать одинаково быстро, но одна рассчитана на бездорожье, другая на город.
Пример:
-- PostgreSQL: расширенные возможности в одном запросеSELECT u.id, jsonb_build_object('email', u.email) AS profile_jsonFROM users u;-- MySQL: похожая задача, но через свой набор функцийSELECT u.id, JSON_OBJECT('email', u.email) AS profile_jsonFROM users u;🔎 Как это происходит на практике:
- Контекст: команда проектирует новый сервис с аналитическими отчётами и API.
- Действия: сравнивают не только скорость CRUD, но и встроенные возможности платформы.
- Результат: архитектурные решения становятся осознанными, а не «по привычке».
Характеристики:
- философия влияет на формат схем и запросов;
- переносимость достигается через адаптацию, а не копипаст;
- цена ошибки — рост техдолга при миграциях.
Когда использовать:
На этапе выбора стека и при ревью архитектурных решений.
✅ Вывод:
Понимание философии СУБД помогает выбирать корректные компромиссы ещё до написания большого объёма SQL.
🧪 2. MVCC на простом уровне
MVCC — ключевая идея для конкурентной работы: чтения не должны останавливать записи, а записи не должны парализовать чтения.
И PostgreSQL, и InnoDB в MySQL используют версионный подход, но детали реализации отличаются.
И PostgreSQL, и InnoDB в MySQL используют версионный подход, но детали реализации отличаются.
🟢 Если совсем просто:
MVCC = база хранит версии строк, чтобы параллельные запросы меньше мешали друг другу.
🎯 Как понять, что этап прошёл успешно:
Вы можете объяснить, почему при высокой нагрузке важны не только индексы, но и модель конкурентности.
Назначение:
Снизить взаимные блокировки между читателями и писателями.
Простыми словами:
Пока одна транзакция меняет строку, другая может читать «подходящую» версию.
Для новичка:
MVCC не отменяет блокировки полностью, но сильно уменьшает количество конфликтов в типовых сценариях.
Аналогия:
Это как читать копию документа, пока редактор вносит изменения в новую версию.
Пример:
BEGIN;UPDATE accountsSET balance = balance - 100WHERE id = 10;-- COMMIT позже-- Транзакция B в это же время читает данныеSELECT a.id, a.balanceFROM accounts aWHERE a.id = 10;🔎 Как это происходит на практике:
- Контекст: API с частыми чтениями профилей и периодическими обновлениями.
- Действия: проектируют запросы и транзакции с учётом MVCC-модели.
- Результат: меньше «зависаний» из-за лишних блокировок.
Характеристики:
- подход версионности есть в обеих СУБД;
- поведение зависит от уровня изоляции и типа запроса;
- архитектурно это база для стабильной OLTP-нагрузки.
Когда использовать:
Всегда, когда в системе много параллельных чтений и записей.
✅ Вывод:
MVCC — базовый must-know для понимания «почему база иногда ведёт себя не так, как ожидают без учёта конкурентности».
🏗️ 3. Storage engines в MySQL и роль InnoDB
В PostgreSQL архитектура хранения более монолитная: нет ежедневного выбора «движка» для каждой таблицы.
В MySQL historically важная тема — storage engines, и в современных OLTP-проектах почти всегда берут InnoDB.
В MySQL historically важная тема — storage engines, и в современных OLTP-проектах почти всегда берут InnoDB.
🟢 Если совсем просто:
Для MySQL по умолчанию думайте про InnoDB, если нужен нормальный транзакционный OLTP.
🎯 Как понять, что этап прошёл успешно:
Вы чётко знаете, почему в проекте выбран InnoDB и какие риски у несоответствующего engine.
Назначение:
Выбрать корректный механизм хранения и транзакционного поведения в MySQL.
Простыми словами:
Engine определяет, как таблица хранится и как работает с транзакциями/блокировками.
Для новичка:
Если без особых причин выбран не InnoDB, это повод проверить архитектурное решение.
Аналогия:
Это как выбрать тип коробки передач для машины: внешне поездка та же, но поведение под нагрузкой разное.
Пример:
-- MySQL: явно фиксируем engine при создании таблицыCREATE TABLE orders ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, status VARCHAR(32) NOT NULL) ENGINE=InnoDB;-- Проверка движка таблицы в MySQLSHOW TABLE STATUS LIKE 'orders';🔎 Как это происходит на практике:
- Контекст: у команды проблемы с целостностью/блокировками после legacy-настроек.
- Действия: инвентаризируют таблицы и приводят engine к InnoDB.
- Результат: поведение транзакций становится предсказуемым.
Характеристики:
- InnoDB — де-факто стандарт для транзакционных нагрузок в MySQL;
- выбор engine влияет на возможности и ограничения;
- при миграции legacy-проектов нужно отдельно проверять эту часть.
Когда использовать:
При проектировании MySQL-схемы и аудите существующих таблиц.
✅ Вывод:
Storage engine в MySQL — это архитектурный выбор, а не «деталь, которую можно игнорировать».
🔢 4. SERIAL vs AUTO_INCREMENT
И PostgreSQL, и MySQL умеют автоинкрементные id, но синтаксис и механика отличаются.
В PostgreSQL
В PostgreSQL
SERIAL — исторический сахар над sequence, а в MySQL используется AUTO_INCREMENT.🟢 Если совсем просто:
Смысл один: числовой id растёт автоматически, но оформляется в разных диалектах по-разному.
🎯 Как понять, что этап прошёл успешно:
Вы уверенно переводите схему id между PostgreSQL и MySQL без потери семантики.
Назначение:
Обеспечить простой и стабильный генератор первичных ключей.
Простыми словами:
База сама выдаёт следующий id при вставке.
Для новичка:
В PostgreSQL в новых схемах часто выбирают
GENERATED ... AS IDENTITY, но SERIAL всё ещё широко встречается.Аналогия:
Это как электронная очередь: номер выдаётся автоматически, оператор его вручную не назначает.
Пример:
-- PostgreSQL: классический вариантCREATE TABLE users ( id SERIAL PRIMARY KEY, email TEXT NOT NULL UNIQUE);-- MySQL: автоинкремент на колонкеCREATE TABLE users ( id BIGINT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL UNIQUE);🔎 Как это происходит на практике:
- Контекст: миграция схемы пользователей между двумя СУБД.
- Действия: адаптируют DDL под нативный стиль id-генерации.
- Результат: CRUD работает предсказуемо, без ручной генерации ключей.
Характеристики:
- идея автоинкремента одинакова;
- синтаксис и детали DDL различаются;
- важно учитывать стратегию id в API и интеграциях.
Когда использовать:
В большинстве внутренних OLTP-таблиц.
✅ Вывод:
SERIAL и AUTO_INCREMENT решают одну задачу, но требуют диалектно корректного оформления.🧾 5. JSON vs JSONB
В MySQL есть тип
На практике в PostgreSQL для рабочих нагрузок часто выбирают
JSON, в PostgreSQL есть JSON и JSONB.На практике в PostgreSQL для рабочих нагрузок часто выбирают
JSONB, потому что он удобнее для индексации и поиска.🟢 Если совсем просто:
Если по JSON нужно часто фильтровать, в PostgreSQL обычно берут
JSONB.🎯 Как понять, что этап прошёл успешно:
Вы можете обосновать, когда JSON оставляем как «payload», а когда поля выносим в отдельные колонки.
Назначение:
Хранить полуструктурированные данные без мгновенного взрыва числа колонок.
Простыми словами:
JSON удобен для гибких атрибутов, но не должен заменять нормальную реляционную модель.
Для новичка:
Ключевые связи (
user_id, order_id, статусы домена) не прячем в JSON-полях.Аналогия:
JSON — это «дополнительная папка с вложениями», а не место для основного договора.
Пример:
-- PostgreSQL: JSONB + индекс под фильтрациюCREATE TABLE events ( id BIGSERIAL PRIMARY KEY, payload JSONB NOT NULL); CREATE INDEX idx_events_payload_ginON events USING GIN (payload);-- MySQL: JSON-колонка с выборкой через путьSELECT e.id, JSON_EXTRACT(e.payload, '$.source') AS sourceFROM events e;🔎 Как это происходит на практике:
- Контекст: система принимает внешние события с неполной схемой.
- Действия: метаданные кладут в JSON/JSONB, ключевые поля оставляют реляционными.
- Результат: схема остаётся управляемой, а гибкость не теряется.
Характеристики:
- JSON удобен для гибких атрибутов;
- JSONB в PostgreSQL обычно лучше для частых фильтров;
- при активной фильтрации по одному полю часто выгодно вынести его в отдельную колонку.
Когда использовать:
Для event payload, metadata и умеренно динамичных атрибутов.
✅ Вывод:
JSON-поля полезны как дополнение к модели, но не как замена нормальной структуре таблиц.
🧪 6. Краткое сравнение по теме
| Кейс | PostgreSQL | MySQL |
|---|---|---|
| Общая философия | Богатый SQL toolkit, расширяемость | Простота старта, прагматичный web-фокус |
| Конкурентность | MVCC (свои детали реализации) | MVCC в InnoDB |
| Хранение | Единая архитектура движка | Storage engines, в OLTP обычно InnoDB |
| Автоинкремент | SERIAL / IDENTITY | AUTO_INCREMENT |
| JSON | JSON, JSONB (часто выбирают JSONB) | JSON |
✅ Вывод:
Различия архитектуры не отменяют переносимость, но требуют осознанных диалектных решений.
🧠 Must-Know (запомнить)
- Архитектура СУБД влияет на поведение запросов не меньше, чем синтаксис.
- MVCC в обеих СУБД важен для конкурентной OLTP-нагрузки.
- В MySQL для транзакционных таблиц ориентир — InnoDB.
SERIALиAUTO_INCREMENTрешают одну задачу, но оформляются по-разному.- В PostgreSQL
SERIALисторический сахар; в новых схемах часто используютIDENTITY. - В PostgreSQL
JSONBобычно практичнее для фильтрации и индексов. - Ключевые реляционные связи не должны жить внутри JSON.
- Архитектурные решения нужно документировать в команде.
✅ Вывод:
Junior-уровень по архитектуре = понимать базовые механики и выбирать безопасные дефолты.
❌ Частые мифы
❌ Миф: PostgreSQL и MySQL отличаются только синтаксисом.
✅ Как правильно: Архитектурные решения (MVCC, engine, JSON-модель) тоже сильно влияют на поведение системы.
📎 Почему это важно: Иначе миграции и ревью схемы превращаются в серию неожиданных багов.
❌ Миф: MVCC полностью убирает блокировки.
✅ Как правильно: MVCC снижает конфликты, но блокировки и конфликты транзакций всё равно возможны.
📎 Почему это важно: Неправильные ожидания ведут к неверному проектированию транзакций.
❌ Миф: В MySQL engine не имеет значения.
✅ Как правильно: Для OLTP engine критичен; в большинстве случаев нужен InnoDB.
📎 Почему это важно: Неверный engine бьёт по транзакционности и предсказуемости.
❌ Миф: JSON можно использовать вместо нормализации везде.
✅ Как правильно: JSON полезен для гибких полей, но ключевые данные и связи храним реляционно.
📎 Почему это важно: Иначе ломаются индексы, целостность и поддерживаемость модели.
🎤 Часто спрашивают на собеседованиях
❓ Вопрос: В чём философская разница PostgreSQL и MySQL на прикладном уровне?
✅ Ответ: PostgreSQL чаще выбирают за богатый SQL/расширяемость, MySQL — за практичную простоту и массовый production-опыт в веб-системах.
❓ Вопрос: Что такое MVCC простыми словами?
✅ Ответ: Это модель, где СУБД работает с версиями строк, чтобы чтения и записи меньше блокировали друг друга.
❓ Вопрос: Зачем в MySQL вообще знать про storage engines?
✅ Ответ: Потому что engine определяет важные свойства таблиц; для OLTP обычно нужен InnoDB.
❓ Вопрос: Чем отличается
SERIAL от AUTO_INCREMENT?
✅ Ответ: Оба дают автоинкрементный id, но SERIAL — PostgreSQL-обёртка над sequence, а AUTO_INCREMENT — механизм MySQL.❓ Вопрос: Почему в PostgreSQL часто выбирают
JSONB?
✅ Ответ: Обычно из-за удобной индексации и более практичной работы с частыми JSON-фильтрами.❓ Вопрос: Можно ли хранить внешние ключи в JSON?
✅ Ответ: Технически можно, но это плохая практика: теряется явная реляционная целостность и усложняется поддержка.
❓ Вопрос: Что важнее при выборе между PostgreSQL и MySQL: синтаксис или архитектура?
✅ Ответ: Важны оба слоя, но архитектура задаёт долгосрочное поведение системы под нагрузкой.
🚨 Типичные ошибки
- Считать, что перенос SQL между PostgreSQL и MySQL — это только замена синтаксиса.
- Игнорировать MVCC и проектировать транзакции без учёта конкурентности.
- Не проверять engine таблиц в MySQL.
- Путать
SERIALиAUTO_INCREMENTпри миграциях схемы. - Складывать ключевые доменные поля в JSON вместо нормальных колонок.
- Не документировать архитектурные решения команды.
✅ Вывод:
Большинство проблем в теме связаны не со «сложностью SQL», а с неявными архитектурными допущениями.
✅ Best Practices
- Для новой команды фиксируйте короткую архитектурную памятку: MVCC, engine, id-стратегия, JSON-правила.
- В MySQL-проектах явно проверяйте, что транзакционные таблицы работают на InnoDB.
- В PostgreSQL для новых схем рассматривайте
IDENTITYкак современную альтернативуSERIAL. - Для JSON-полей заранее определяйте: это payload или рабочий фильтр с индексом.
- Ключевые связи и бизнес-инварианты храните в реляционных колонках.
- Архитектурные решения проверяйте на реальных сценариях чтения/записи, а не только на синтаксисе DDL.
✅ Вывод:
Правильные дефолты в архитектуре дают больше эффекта, чем точечные оптимизации в конце проекта.
🧾 Заключение
Эта тема закрывает базовый архитектурный минимум для сравнения PostgreSQL и MySQL без перегруза.
Если вы понимаете философию платформ, роль MVCC, InnoDB, отличия автоинкремента и выбор JSON-формата, вы уже принимаете более зрелые инженерные решения.
Если вы понимаете философию платформ, роль MVCC, InnoDB, отличия автоинкремента и выбор JSON-формата, вы уже принимаете более зрелые инженерные решения.
✅ Вывод:
Архитектурная грамотность уровня junior+ — это умение выбрать безопасный дефолт и объяснить его команде.