Базы данных — backend на Go живёт ровно настолько хорошо, насколько вы знаете СУБД
Сервис на Go редко бывает сложным сам по себе — горутина приняла запрос, сходила в PostgreSQL, отдала ответ. Вся настоящая сложность, вся борьба за корректность и производительность сосредоточена в том, как вы обращаетесь с базой данных. database/sql и pgx дают вам прямой доступ к SQL, но не защищают ни от одной концептуальной ошибки: неверный индекс, неверный уровень изоляции, неверный порядок блокировок — всё это компилируется, проходит локальные тесты и проявляется только под параллельной нагрузкой.
Ловушки тут не про синтаксис. Кандидаты считают индекс бесплатным ускорителем чтения и забывают про его цену на каждом INSERT. Путают нормализацию со сжатием. Думают, что Read Committed спасает от фантомных чтений. Берут SELECT FOR UPDATE, полагая, что это разделяемая блокировка чтения. Ждут, что приложение должно само ловить deadlock. Эта тема разбирает СУБД по слоям — от устройства B-tree до журнала упреждающей записи — так, чтобы каждый из этих вопросов на собеседовании вы закрывали механизмом, а не заученной фразой.
Карта темы
- Основы индексов — что такое индекс, почему B-tree, и какую цену он берёт на каждой записи.
- Нормализация — 1NF/2NF/3NF, хранение каждого факта один раз и устранение аномалий обновления.
- Типы индексов PostgreSQL — B-tree, Hash, GIN, GiST, BRIN, SP-GiST и нагрузка, под которую заточен каждый.
- Денормализация — намеренная избыточность ради сокращения join на пути чтения и её цена при записи.
- Изоляция транзакций — четыре уровня, три аномалии и устойчивость через WAL.
- Документные vs реляционные базы — гибкая схема и встраивание против join, ACID и ссылочной целостности.
- Блокировка строк —
SELECT FOR UPDATE, порядок захвата блокировок и взаимоблокировки.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
| Считать индекс бесплатным ускорителем чтения | Упустить цену записи — каждый INSERT/UPDATE дополнительно правит индекс |
| Считать, что любой индекс — это B-tree | Промахнуться с типом — jsonb и массивам нужен GIN, не B-tree |
Использовать Hash для запроса диапазона | Hash обслуживает только равенство; диапазон уйдёт в seq scan |
| Путать нормализацию со сжатием данных | Неверная ментальная модель — это дисциплина проектирования схемы, не storage |
| Денормализовать преждевременно, без профиля | Усложнённая запись и риск рассинхрона без доказанной выгоды чтения |
Думать, что Read Committed ловит фантомные чтения | Скрытый баг — фантомы запрещает только Repeatable Read и выше |
Считать Serializable слабейшим уровнем изоляции | Перевёрнутая модель — Serializable сильнейший, Read Uncommitted слабейший |
Считать SELECT FOR UPDATE разделяемой блокировкой чтения | Это эксклюзивная блокировка строки на запись — конкурентные писатели ждут |
| Захватывать строки в разном порядке в разных транзакциях | Взаимоблокировка — каждая транзакция ждёт строку, удерживаемую другой |
| Думать, что WAL пишется после страниц данных | Перевёрнутый порядок — без записи WAL до данных нет устойчивости и восстановления |
Значение для собеседований
Базы данных — обязательная тема на любом backend-интервью, и спрашивают не «знаешь ли ты слово индекс», а умеешь ли ты рассуждать о цене, корректности и конкурентности.
Что обычно проверяют:
- Что такое индекс, почему B-tree по умолчанию и какую цену он берёт на записи.
- Зачем нормализуют схему — устранение избыточности и аномалий, а не скорость запросов.
- Когда осознанно денормализуют и чем за это платят на стороне записи.
- Какие типы индексов есть в PostgreSQL и под какую нагрузку заточен каждый.
- Четыре уровня изоляции, три аномалии и какой уровень что запрещает.
- Что делает
SELECT FOR UPDATEи почему противоположный порядок захвата даёт deadlock. - Чем документная база отличается от реляционной и какие гарантии вы теряете.
- Как WAL обеспечивает устойчивость и восстановление после сбоя.
Типичный неверный ответ: «Read Committed — это безопасный уровень, он защищает от всех аномалий чтения». Это запускает разбор того, что Read Committed запрещает только грязное чтение, а неповторяющиеся и фантомные чтения на нём по-прежнему возможны, и что выбор уровня — это сознательный компромисс между корректностью и конкурентностью.