Реляционные базы данных — одна из тех технологий, которые кажутся «базой из базы», но именно на них чаще всего ломается продакшн. Почти любой разработчик пишет SQL-запросы, но далеко не каждый понимает, как именно база исполняет эти запросы, почему иногда всё работает быстро, а иногда внезапно медленно, и что вообще стоит за словами вроде ACID или нормализация.
Разобраться в этом полезно не только backend-разработчикам. Любой, кто работает с данными, от фронтенда до аналитики, выигрывает, когда понимает, как устроена реляционная модель.
Начнём с самого фундамента.
Разобраться в этом полезно не только backend-разработчикам. Любой, кто работает с данными, от фронтенда до аналитики, выигрывает, когда понимает, как устроена реляционная модель.
Начнём с самого фундамента.
Что вообще значит «реляционная» база
Реляционная модель — это способ хранения данных в виде таблиц, связанных между собой через ключи. Каждая таблица — это сущность (например, пользователи), а строки — её экземпляры. Связи между таблицами задаются через внешние ключи.
На практике это выглядит так:
users
- id
- name
orders
- id
- user_id
- amount
user_id в таблице orders ссылается на users.id. Это и есть реляционная связь.
Важно: реляционная модель — это не просто таблицы, а строгая математическая модель. И именно из неё вытекают SQL, нормальные формы и всё остальное.
На практике это выглядит так:
users
- id
- name
orders
- id
- user_id
- amount
user_id в таблице orders ссылается на users.id. Это и есть реляционная связь.
Важно: реляционная модель — это не просто таблицы, а строгая математическая модель. И именно из неё вытекают SQL, нормальные формы и всё остальное.
ANSI SQL: общий язык, но с акцентами
SQL — это стандартный язык работы с реляционными базами. ANSI SQL — попытка сделать единый стандарт, чтобы один и тот же запрос работал в разных СУБД.
Простой пример:
Простой пример:
Он будет работать почти везде: PostgreSQL, MySQL, SQLite.
Но на практике каждая СУБД добавляет свои особенности:
• PostgreSQL: мощная работа с JSON, оконные функции
• MySQL: упрощённые конструкции, но исторически слабее с транзакциями (в старых версиях)
• SQLite: лёгкость и встраиваемость
Поэтому важно понимать: SQL — один, но диалекты разные. Если вы пишете сложную бизнес-логику на уровне базы, вы привязываетесь к конкретной СУБД.
Но на практике каждая СУБД добавляет свои особенности:
• PostgreSQL: мощная работа с JSON, оконные функции
• MySQL: упрощённые конструкции, но исторически слабее с транзакциями (в старых версиях)
• SQLite: лёгкость и встраиваемость
Поэтому важно понимать: SQL — один, но диалекты разные. Если вы пишете сложную бизнес-логику на уровне базы, вы привязываетесь к конкретной СУБД.
ACID: гарантия того, что база не «сломается»
ACID — это четыре свойства, которые делают транзакции надёжными:
• Atomicity (атомарность): всё или ничего
• Consistency (согласованность): данные всегда валидны
• Isolation (изолированность): транзакции не мешают друг другу
• Durability (надёжность): после коммита данные не исчезнут
На практике ACID — это не абстракция, а вполне конкретные гарантии, которые спасают от трудноуловимых багов. Например, при работе с платежами или балансами пользователей нарушение атомарности или согласованности почти гарантированно приведёт к некорректным данным.
• Atomicity (атомарность): всё или ничего
• Consistency (согласованность): данные всегда валидны
• Isolation (изолированность): транзакции не мешают друг другу
• Durability (надёжность): после коммита данные не исчезнут
На практике ACID — это не абстракция, а вполне конкретные гарантии, которые спасают от трудноуловимых багов. Например, при работе с платежами или балансами пользователей нарушение атомарности или согласованности почти гарантированно приведёт к некорректным данным.
Транзакции: когда «либо всё, либо ничего»
Транзакция — это группа операций, которая должна выполниться целиком или не выполниться вообще.
Классический пример — перевод денег:
Классический пример — перевод денег:
Если что-то пошло не так — откат:
Без транзакций вы рискуете получить ситуацию, где деньги списались, но не зачислились.
Нормальные формы: почему «разбивать таблицы» — это не занудство
Нормализация — это способ структурировать данные так, чтобы не было дублирования и не возникало аномалий при обновлении.
Самые важные формы:
1NF (первая нормальная форма): нет массивов и повторяющихся полей
Плохо:
user: "phones = 123, 456"
Хорошо:
user_phones:
- user_id
- phone
2NF — убираем зависимость от части ключа
3NF — убираем транзитивные зависимости
Пример проблемы без нормализации:
orders
- user_name
- user_email
Если пользователь меняет email, вам нужно обновить его во всех заказах.
После нормализации структура становится явной и предсказуемой:
users
- id
- name
- email
orders
- id
- user_id
Теперь данные пользователя хранятся в одном месте, а заказы лишь ссылаются на него через user_id. Это устраняет дублирование и избавляет от необходимости синхронно обновлять одни и те же данные в разных строках.
Самые важные формы:
1NF (первая нормальная форма): нет массивов и повторяющихся полей
Плохо:
user: "phones = 123, 456"
Хорошо:
user_phones:
- user_id
- phone
2NF — убираем зависимость от части ключа
3NF — убираем транзитивные зависимости
Пример проблемы без нормализации:
orders
- user_name
- user_email
Если пользователь меняет email, вам нужно обновить его во всех заказах.
После нормализации структура становится явной и предсказуемой:
users
- id
- name
orders
- id
- user_id
Теперь данные пользователя хранятся в одном месте, а заказы лишь ссылаются на него через user_id. Это устраняет дублирование и избавляет от необходимости синхронно обновлять одни и те же данные в разных строках.
Изоляция транзакций: почему данные «ведут себя странно»
Изоляция — это то, как база ведёт себя, когда несколько транзакций выполняются одновременно. И именно здесь чаще всего возникают неочевидные баги.
Существует несколько уровней изоляции:
• Read Uncommitted: можно читать незакоммиченные данные
• Read Committed: видны только подтверждённые изменения
• Repeatable Read: строки не меняются в рамках транзакции
• Serializable: поведение как будто транзакции выполняются по очереди
Чем выше уровень изоляции, тем меньше аномалий, но тем выше нагрузка на базу.
Простой пример:
Два пользователя одновременно покупают последний товар.
Без достаточной изоляции обе транзакции могут «увидеть», что товар есть в наличии, и обе успешно его купят.
Именно поэтому уровень изоляции — это всегда компромисс между корректностью и производительностью, который выбирается под конкретную задачу.
Существует несколько уровней изоляции:
• Read Uncommitted: можно читать незакоммиченные данные
• Read Committed: видны только подтверждённые изменения
• Repeatable Read: строки не меняются в рамках транзакции
• Serializable: поведение как будто транзакции выполняются по очереди
Чем выше уровень изоляции, тем меньше аномалий, но тем выше нагрузка на базу.
Простой пример:
Два пользователя одновременно покупают последний товар.
Без достаточной изоляции обе транзакции могут «увидеть», что товар есть в наличии, и обе успешно его купят.
Именно поэтому уровень изоляции — это всегда компромисс между корректностью и производительностью, который выбирается под конкретную задачу.
Индексы: почему один запрос работает 1 мс, а другой — 10 секунд
Индекс — это структура данных, которая ускоряет поиск.
Без индекса:
Без индекса:
База делает полный перебор (full scan).
С индексом:
С индексом:
Теперь поиск идёт через структуру вроде B-дерева.
Но есть нюанс: индексы ускоряют чтение, но замедляют запись (INSERT/UPDATE)
Практическая ошибка: разработчик добавляет индекс на каждое поле → база начинает тормозить при записи.
Но есть нюанс: индексы ускоряют чтение, но замедляют запись (INSERT/UPDATE)
Практическая ошибка: разработчик добавляет индекс на каждое поле → база начинает тормозить при записи.
Как всё сходится в реальной системе
В реальном проекте всё взаимосвязано:
• Плохо спроектированная схема → лишние JOIN → медленные запросы
• Отсутствие индексов → full scan → нагрузка на CPU
• Неправильные транзакции → гонки данных
• Игнорирование ACID → баги, которые сложно воспроизвести
Простой пример:
Вы делаете ленту постов:
• Плохо спроектированная схема → лишние JOIN → медленные запросы
• Отсутствие индексов → full scan → нагрузка на CPU
• Неправильные транзакции → гонки данных
• Игнорирование ACID → баги, которые сложно воспроизвести
Простой пример:
Вы делаете ленту постов:
Чтобы это работало быстро, нужен составной индекс:
(user_id, created_at)
Иначе база сначала отфильтрует, потом отсортирует, что дорого.
(user_id, created_at)
Иначе база сначала отфильтрует, потом отсортирует, что дорого.
Когда теория начинает помогать
Понимание реляционных БД даёт неожиданные бонусы:
• Вы начинаете читать execution plan и понимать, что именно делает база при выполнении запроса. Посмотреть его можно с помощью EXPLAIN или EXPLAIN ANALYZE, и вместо догадок вы видите, используются ли индексы, сколько строк перебирается и где возникают узкие места
• Можете объяснить, почему API тормозит
• Проектируете схемы, которые не разваливаются через полгода
• Спокойно обсуждаете архитектуру с backend и DevOps
И самое важное — перестаёте «гадать» и начинаете контролировать поведение базы.
• Вы начинаете читать execution plan и понимать, что именно делает база при выполнении запроса. Посмотреть его можно с помощью EXPLAIN или EXPLAIN ANALYZE, и вместо догадок вы видите, используются ли индексы, сколько строк перебирается и где возникают узкие места
• Можете объяснить, почему API тормозит
• Проектируете схемы, которые не разваливаются через полгода
• Спокойно обсуждаете архитектуру с backend и DevOps
И самое важное — перестаёте «гадать» и начинаете контролировать поведение базы.
Реляционные базы данных — это не просто SQL-запросы. Это:
• Строгая модель данных
• Механизмы гарантии целостности
• Инструменты оптимизации
Если кратко:
• SQL — это язык
• ACID — это надёжность
• Нормализация — это структура
• Индексы — это скорость
И всё это вместе определяет, будет ли ваша система работать стабильно или начнёт сыпаться под нагрузкой.
Хотите узнать больше? Изучите другие статьи из раздела:
• Строгая модель данных
• Механизмы гарантии целостности
• Инструменты оптимизации
Если кратко:
• SQL — это язык
• ACID — это надёжность
• Нормализация — это структура
• Индексы — это скорость
И всё это вместе определяет, будет ли ваша система работать стабильно или начнёт сыпаться под нагрузкой.
Хотите узнать больше? Изучите другие статьи из раздела: