> Каковы преимущества и недостатки индексов в базе данных (Go)

Уровень: senior · Роль: backend · Категория: Технические вопросы

Компании: InDrive, Lamoda

Стек: Go

> Пример ответа

Короткий ответ

Индексы в БД - это структуры данных, ускоряющие поиск и сортировку за счёт дополнительного хранилища и затрат на запись. Основные преимущества: радикальное ускорение SELECT-запросов, обеспечение уникальности, оптимизация JOIN и ORDER BY. Недостатки: замедление INSERT/UPDATE/DELETE, потребление дискового пространства и памяти, риск деградации производительности при неудачном выборе колонок. Индексы - это trade-off между скоростью чтения и скоростью записи, который требует анализа паттернов доступа к данным.

Подробное объяснение

Индекс в реляционной БД - это отдельная структура (обычно B-tree, hash, или GiST), которая хранит отсортированные значения выбранных колонок со ссылками на строки таблицы. При выполнении запроса оптимизатор решает, использовать ли индекс, исходя из селективности и стоимости операций.

Преимущества:

  • Ускорение поиска по условию WHERE - вместо полного сканирования таблицы (O(n)) получаем логарифмический поиск (O(log n)).
  • Оптимизация JOIN - индексы на ключах соединения позволяют использовать nested loop или hash join эффективнее.
  • Быстрая сортировка и группировка - если индекс уже отсортирован, ORDER BY и GROUP BY не требуют дополнительной сортировки.
  • Обеспечение уникальности - уникальный индекс гарантирует отсутствие дубликатов.
  • Покрывающие индексы - если все нужные колонки входят в индекс, можно не обращаться к таблице (index-only scan).

Недостатки:

  • Замедление операций записи - каждая INSERT, UPDATE, DELETE требует обновления всех индексов на таблице.
  • Дополнительное хранилище - индекс может занимать значительный объём, особенно для больших таблиц с длинными ключами.
  • Усложнение планировщика - оптимизатор может ошибиться и выбрать индекс там, где дешевле full scan (например, при низкой селективности).
  • Фрагментация - при частых UPDATE и DELETE индексы деградируют, требуют REINDEX или VACUUM (в PostgreSQL).
  • Накладные расходы на память - кэш индексов конкурирует с кэшем данных.

Когда индексы неэффективны:

  • Колонки с низкой кардинальностью (например, boolean) - индекс почти не сужает выборку.
  • Таблицы малого размера - full scan быстрее, чем обращение к индексу.
  • Частые массовые вставки - индекс становится узким местом.

На практике

В Go-приложениях, работающих с БД, важно проектировать индексы на уровне миграций, а не в коде. Типичный workflow:

  1. Анализируем медленные запросы через EXPLAIN ANALYZE.
  2. Смотрим на фильтры WHERE, JOIN-условия, ORDER BY.
  3. Создаём составные индексы с учётом порядка колонок - сначала колонки с высокой селективностью.
  4. Избегаем лишних индексов на таблицах с высокой частотой записи.
  5. Используем частичные индексы (WHERE-условие в индексе) для редких запросов.
  6. Для Go-приложений с высокой конкурентностью важно помнить: индексы увеличивают время блокировок при записи, что может вызвать contention.

Пример из практики: если у нас таблица orders с миллионами строк и частым запросом SELECT * FROM orders WHERE user_id = $1 AND status = 'active' ORDER BY created_at DESC, оптимальным будет составной индекс (user_id, status, created_at DESC). При этом индекс на user_id отдельно станет избыточным.

Пример кода

Миграция для PostgreSQL через golang-migrate:

SQL
-- создание составного индекса
CREATE INDEX idx_orders_user_status_created
ON orders (user_id, status, created_at DESC);
-- частичный индекс для активных заказов
CREATE INDEX idx_orders_active
ON orders (user_id)
WHERE status = 'active';
-- проверка плана запроса
EXPLAIN ANALYZE
SELECT * FROM orders
WHERE user_id = 123 AND status = 'active'
ORDER BY created_at DESC;

В Go-коде не нужно ничего особенного - индексы работают на стороне БД. Но важно использовать prepared statements и правильные типы параметров, чтобы оптимизатор мог корректно оценить селективность.

Как отвечать на собеседовании

Начните с краткого определения и сразу переходите к trade-off. Покажите системное мышление: свяжите индексы с паттернами доступа, размером данных, частотой записи. Упомяните, что решение об индексе - это всегда компромисс, и его нужно принимать на основе реальных метрик, а не интуиции.

Хорошо, если вы:

  • Приведёте конкретный пример из своего опыта, где индекс ускорил запрос в разы.
  • Упомянете EXPLAIN и важность анализа плана выполнения.
  • Расскажете про отличия B-tree от hash-индексов и когда что использовать.
  • Затронете покрывающие индексы и index-only scan.

Для senior-позиции важно показать понимание внутренностей: как B-tree устроен, что такое селективность, как работает bitmap scan, когда оптимизатор откажется от индекса.

Что проверяет интервьюер

  • Понимание trade-off между чтением и записью.
  • Умение проектировать составные индексы с учётом порядка колонок.
  • Знание, когда индекс бесполезен или вреден.
  • Практический опыт диагностики медленных запросов.
  • Понимание внутреннего устройства индексов (B-tree, hash, GIN, GiST).
  • Способность объяснить влияние индексов на конкурентность и блокировки.

Типичные ошибки

  • Утверждение, что индексы ускоряют все запросы - это не так, они ускоряют только чтение.
  • Игнорирование стоимости записи - на высоконагруженных системах с большим количеством INSERT это критично.
  • Создание индекса на каждой колонке по отдельности вместо составных - это раздувает хранилище и замедляет запись.
  • Непонимание, что порядок колонок в составном индексе имеет значение.
  • Забывают про частичные и покрывающие индексы.
  • Не упоминают необходимость мониторинга и удаления неиспользуемых индексов.
  • Путают кластерный и некластерный индекс (в PostgreSQL кластерный индекс - это просто физическая перестановка строк, а не отдельная структура).

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью