Всем привет!

Я Senior Java Developer в банке, и за эти годы мне довелось пройти немало собеседований. Где-то было спокойно, где-то — как в допросной, с каверзными вопросами, странными задачами и вечным «а почему так, а не иначе?». В процессе я собрал целую коллекцию тем, которые всплывают снова и снова, особенно когда дело касается баз данных.

Сегодня хочу поделиться этим опытом и разобрать вопросы, которые чаще всего задают именно по SQL-базам.

В профиле уже есть другие части для подготовки:

  1. Многопоточность без боли

  2. JVM + Память + GC без боли

  3. Spring без боли

❗❗Дисклеймер❗❗

Эта статья не является учебником по технологиям. Здесь я не буду углубляться в то, как всё работает под капотом или почему это устроено именно так. Это сжатая методичка по вопросам на собеседованиях — только факты, без лишней воды!

Транзакции

Транзакция — это набор операций с базой, который выполняется полностью или совсем не выполняется.

ACID

  • Атомарсность - никакая транзакция не будет зафиксирована в системе частично

  • Согласованность - успешная транзакция всегда приводит данные в корректное, допустимое состояние

  • Изолированность - параллельные транзакции не мешают друг другу и работают так, будто выполняются по очереди

  • Прочность - если транзакция закоммитилась, её данные гарантированно сохранены и не исчезнут

Уровни изоляции

Read uncommitted

Видит зафиксированные и не зафиксированные данные транзакций.

  • PostgreSQL фактически не поддерживает “грязное чтение” (dirty read).

  • Любые SELECT видят только зафиксированные данные, даже если указан Read Uncommitted. Это официально: в PostgreSQL Read Uncommitted ведёт себя как Read Committed.

Read committed

каждая операция внутри транзакции видит только те данные, которые были зафиксированы к моменту выполнения конкретного запроса, а не к началу транзакции

Read committed
Read committed

Repeatable read

  • В начале транзакции PostgreSQL делает снимок состояния БД (snapshot).

  • Все последующие запросы этой транзакции читают одну и ту же консистентную картину, независимо от того, что успели закоммитить другие транзакции.

  • Поэтому non-repeatable reads отсутствуют — одна и та же выборка всегда возвращает одинаковые данные в рамках транзакции.

  • реализация Repeatable Read в PostgreSQL не допускает фантомных чтений

Serializable

Она гарантирует, что даже при конкурентном выполнении транзакций результат останется таким же, как и в случае их последовательного (по одной за раз) выполнения, без всякой конкурентности.

  • Читающие транзакции не мешают друг другу — ты можешь спокойно читать данные, пока кто-то другой их меняет.

  • Параллельные транзакции записи тоже не блокируют друг друга, если они работают с разными объектами.

  • Блокировка возникает только тогда, когда две транзакции пытаются изменить один и тот же объект одновременно.

Вот очень полезная табличка из официальной доки Postgres

https://www.postgresql.org/docs/current/transaction-iso.html

Индексы

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

Важно отметить, что индексы могут также замедлять добавление, удаление и изменение данных, так как при этом операции также требуется обновление соответствующих индексов

Хеш-индексы

Хранилища типа ключ — значение работают как словари в языках программирования. Обычно реализуются через хеш-таблицу, где каждому ключу соответствует адрес значения в файле данных.

При добавлении или обновлении пары «ключ — значение» хеш-таблица обновляется, чтобы хранить правильный адрес. А чтобы прочитать значение, система просто смотрит адрес в хеш-таблице и обращается к файлу — быстро и эффективно.

SS-таблицы и LSM-деревья

SS-таблица (SSTable) — это файл, где ключи отсортированы и каждый встречается только один раз. Такая структура облегчает слияние сегментов даже больших файлов, не загружая всю таблицу в память, и работает по принципу сортировки слиянием.

Чтобы найти нужный ключ, не обязательно держать все индексы в памяти: достаточно знать соседние ключи и просматривать файл между ними до нужного значения.

SS-таблицы и LSM-деревья
SS-таблицы и LSM-деревья

B-деревья

B-дерево — самая распространённая индексная структура. Как и SSTable, оно хранит пары «ключ — значение» в отсортированном виде, что позволяет быстро искать ключи и выполнять диапазонные запросы.

Но в отличие от SSTable, B-дерево делит данные на страницы фиксированного размера (обычно 4 КБ), которые читаются и записываются по одной. Каждая страница хранит ключи и ссылки на дочерние страницы, формируя дерево. Поиск всегда начинается с корневой страницы и проходит через страницы, отвечающие за диапазоны ключей, пока не найдётся нужное значение.

B-деревья
B-деревья

Разница между индексами (user_id, status) и (user_id) INCLUDE (status)

Составной индекс (user_id, status)

1.     Структура: Оба поля являются частью ключа индекса

2.     Сортировка: Данные сортируются сначала по user_id, затем по status

3.     Использование: Эффективен для -

o    Запросов с условиями на user_id

o    Запросов с условиями на user_id И status

o    Сортировок по этим полям

Индекс с INCLUDE (user_id) INCLUDE (status)

1.     Структура: Только user_idв ключе индекса, status хранится в листьях индекса

2.     Сортировка: Данные сортируются только по user_id

3.     Использование: Эффективен для:

o    Запросов с условиями только на user_id

o    Запросов, где нужны оба поля (покрывающий индекс)

o    Не поддерживает поиск/сортировку по status отдельно

Ключевые различия

1.     Размер: INCLUDE-индекс обычно компактнее

2.     Гибкость: Составной индекс поддерживает больше типов запросов

3.     Производительность: Для запросов с фильтрацией по обоим полям составной индекс работает лучше

4.     Поддержка: INCLUDE синтаксис доступен не во всех СУБД (появился в PostgreSQL 11, SQL Server и др.)

Алгоритм вешанья индексов

Это чистой мой алгоритм, я не говорю, что он САМЫЙ ВЕРНЫЙ и нет других)

Алгоритм вешанья индексов

1) Определить цель индексирования: нужно понять, для каких операций или запросов индекс будет использоваться. Например, это может быть поиск по определенному полю, сортировка или слияние данных.

2) Оценить затраты и выгоды: провести анализ текущей структуры базы данных и определить, какие данные и операции будут выигрывать от индексирования. Оценить затраты на создание и поддержку индекса.

3) Анализ запросов: изучить типичные запросы, которые будут выполняться в базе данных, и выяснить, какие поля или комбинации полей чаще всего используются в условиях WHERE или ORDER BY. Это поможет определить, какие поля следует индексировать.

4) Избегать избыточности: создание индексов для каждого поля не всегда является эффективным решением. Определить комбинации полей, которые часто используются в запросах, и создать составные индексы для них.

5) Определить порядок сортировки: для поля, по которому будет выполняться сортировка или группировка, определить порядок и создать индекс соответствующего типа.

6) Определить уникальность: если поле должно содержать только уникальные значения, создать уникальный индекс для этого поля.

7) Обновление и поддержка индекса: учесть, что каждая операция добавления, обновления или удаления данных влияет на индексирование. Обновление индексов может занимать время и ресурсы, поэтому важно оптимизировать этот процесс.

8) Мониторинг и оптимизация: регулярно производить мониторинг производительности базы данных и запросов. Если производительность снижается, можно рассмотреть возможность создания или изменения индексов для улучшения выполнения запросов.

Репликация

Репликация — это копирование данных с одной базы на другие.

  • Зачем: для отказоустойчивости и высокой доступности.

  • Виды: синхронная (данные пишутся одновременно на все реплики) и асинхронная (реплики обновляются с задержкой).

  • Реплика может использоваться для чтения, но запись идёт в мастер-базу.

Шардирование

Шардирование — это горизонтальное деление данных между разными серверами.

  • Каждая шард содержит часть данных (например, по диапазону ключей или хешу).

  • Позволяет масштабировать систему: нагрузка на запись и чтение распределяется между несколькими инстансами.

Секционирование

Секционирование — это деление таблицы внутри одной базы на логические части (секции).

  • Например, по дате: январь, февраль, март.

  • Упрощает поиск и может ускорять некоторые запросы, но все данные остаются в одной БД, в отличие от шардирования.

SQL vs NoSQL

SQL (реляционные БД)

  • Структура: таблицы с фиксированными колонками и строками.

  • Язык: SQL, строгие схемы (schema).

  • Гарантии: ACID, транзакции.

  • Когда применять: сложные связи между данными, строгие требования к консистентности, аналитика, банки, ERP.

NoSQL (нереляционные БД)

  • Структура: документы, ключ‑значение, графы или колоночные хранилища.

  • Гибкие схемы, горизонтальное масштабирование проще.

  • Когда применять: большие объёмы данных, высокая нагрузка на запись, быстрый поиск по ключу, распределённые системы, кэш, логи, IoT.

Когда можно использовать денормализацию бд

Денормализация — это сознательное объединение данных, которые обычно хранятся в разных таблицах, чтобы уменьшить количество соединений (JOIN) и ускорить запросы.

Используют, когда:

  • Часто выполняются сложные запросы с большим количеством JOIN, и это становится узким местом по производительности.

  • Нужна оптимизация чтения в системах с высокой нагрузкой на SELECT.

  • Можно пожертвовать скоростью записи ради ускорения чтения (так как обновление денормализованных данных сложнее).

VIEW

  • Это виртуальная таблица, которая хранит только запрос, а не данные.

  • Данные берутся из базовых таблиц каждый раз при обращении.

  • Плюс: удобство, безопасность, можно скрыть сложные JOIN’ы.

  • Минус: запросы могут быть медленнее, если базовые таблицы большие.

Materialized VIEW

  • Это настоящая таблица, которая хранит результат запроса.

  • Нельзя вставлять, обновлять или удалять строки напрямую — изменения происходят только через REFRESH MATERIALIZED VIEW.

  • Плюс: быстрый доступ к данным, не нужно пересчитывать результат при каждом запросе.

  • Минус: нужно периодически обновлять, чтобы данные оставались актуальными

Итог

Сегодня мы рассмотрели ключевые аспекты работы с базами данных: структуру таблиц, индексы, транзакции, запросы. Все это часто встречается на собеседованиях по SQL и работе с СУБД. Список тем составлен на основе моего опыта и опыта коллег, проходивших собеседования на позиции от Junior до Senior.

Всем спасибо за внимание, удачных собесов и хорошего дня!)

Комментарии (1)


  1. vvm13xx
    22.11.2025 18:15

    -