> Где хранится индекс и как к нему обращаются (Go)

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

Компании: Сбер

Стек: Go

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

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

Индекс хранится в отдельной структуре данных на диске и в памяти, отдельно от самих данных таблицы. В PostgreSQL это, как правило, B-tree, в MySQL InnoDB - тоже B-tree, но встроенный в кластеризованный индекс. Доступ к индексу происходит через планировщик запросов: он выбирает индекс на основе статистики, затем движок выполняет поиск по дереву (O(log n)) и получает ссылки на строки (TID или primary key), после чего идёт чтение самих строк из heap-файла. В Go-приложениях доступ к индексу обычно скрыт за драйвером БД, но понимание этого процесса критично для оптимизации запросов.

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

Индекс - это отдельная структура, физически хранящаяся в собственных файлах (например, _idx в PostgreSQL) или в отдельном tablespace. Он не хранит данные строк, а содержит ключевые значения и указатели на строки.

Классическая схема для B-tree:

  • root page - вершина дерева
  • internal pages - промежуточные узлы с диапазонами ключей
  • leaf pages - конечные узлы, содержащие пары (ключ, TID) или (ключ, первичный ключ)

Доступ к индексу происходит в несколько этапов:

  1. Парсер и планировщик анализируют запрос, смотрят на статистику (pg_statistic, information_schema.statistics).
  2. Планировщик оценивает селективность условия WHERE и решает, использовать ли индекс.
  3. Если индекс выбран, движок выполняет поиск по дереву: спускается от корня к листьям, сравнивая ключи.
  4. Находит нужные листовые записи, извлекает TID (или PK).
  5. Выполняет чтение строк из heap-файла по TID (это называется "table access by index rowid").
  6. Если запрос покрывается индексом (covering index), шаг 5 пропускается - данные берутся прямо из листа.

В MySQL InnoDB ситуация отличается: первичный ключ - это кластеризованный индекс, где листья содержат сами строки. Вторичные индексы хранят PK, а не TID, и для доступа к строке выполняется дополнительный lookup по кластеризованному индексу.

В Go-приложениях доступ к индексу происходит через SQL-запросы, которые отправляются в БД. Сам Go-код не управляет индексами напрямую - это делает СУБД. Однако важно понимать, как индексы влияют на производительность, чтобы писать эффективные запросы и правильно проектировать схему.

На практике

Для backend-разработчика на Go практическое значение сводится к нескольким моментам:

  • Индексы создаются через миграции (например, golang-migrate, goose), а не через ORM-автомиграции в проде.
  • При написании запросов нужно учитывать, какие индексы существуют: условия WHERE, ORDER BY, JOIN должны использовать префиксы составных индексов.
  • В Go-коде важно использовать prepared statements, чтобы планировщик мог кэшировать план запроса с учётом индексов.
  • Для высоконагруженных систем стоит проверять план запроса через EXPLAIN (в Go это можно делать через отдельный клиент или вручную в psql).
  • Покрывающие индексы позволяют избежать чтения heap-файла - это особенно полезно для частых OLTP-запросов.

Пример: если есть таблица users(id, email, name) и запрос SELECT name FROM users WHERE email = $1, то индекс на email позволит найти строки, но для получения name потребуется чтение heap. Если создать индекс (email, name), то name будет взят прямо из индекса.

Пример кода

GO
// Миграция для создания индекса (пример с golang-migrate)
// 000001_create_users_index.up.sql
CREATE INDEX idx_users_email ON users (email);
// 000001_create_users_index.down.sql
DROP INDEX idx_users_email;
// Go-код: использование prepared statement для доступа к индексу
func GetUserByEmail(ctx context.Context, db *sql.DB, email string) (*User, error) {
// Prepared statement позволяет планировщику переиспользовать план
stmt, err := db.PrepareContext(ctx, "SELECT id, name FROM users WHERE email = $1")
if err != nil {
return nil, err
}
defer stmt.Close()
var u User
err = stmt.QueryRowContext(ctx, email).Scan(&u.ID, &u.Name)
if err != nil {
return nil, err
}
return &u, nil
}
// Проверка плана запроса (выполняется в psql или через отдельный клиент)
// EXPLAIN SELECT id, name FROM users WHERE email = 'test@example.com';

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

Начните с краткого определения: индекс - это отдельная структура, ускоряющая поиск. Затем опишите физическое хранение: B-tree, отдельные файлы, листовые страницы с парами (ключ, TID). Обязательно упомяните роль планировщика: он решает, использовать ли индекс, на основе статистики. Расскажите про разницу между кластеризованными и некластеризованными индексами (InnoDB vs PostgreSQL). Если спросят про Go - подчеркните, что доступ к индексу прозрачен для приложения, но важно писать запросы, которые могут использовать индексы, и проверять планы через EXPLAIN.

Хорошо добавить пример из практики: когда индекс не используется (например, из-за функции на колонке или несоответствия типов) и как это диагностировать.

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

Интервьюер оценивает:

  • Понимание внутреннего устройства индексов (B-tree, leaf pages, TID).
  • Знание процесса планирования запросов и роли статистики.
  • Умение объяснить разницу между кластеризованными и вторичными индексами.
  • Практические навыки: когда индекс полезен, а когда вреден (overhead на запись, размер).
  • Понимание покрывающих индексов и index-only scans.
  • Способность связать теорию с реальной работой backend-разработчика на Go.

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

  • Утверждение, что индекс хранится вместе с данными - это неверно для большинства СУБД (кроме кластеризованных).
  • Игнорирование роли планировщика: кандидат думает, что индекс используется всегда.
  • Непонимание разницы между TID и первичным ключом в вторичных индексах.
  • Утверждение, что индекс ускоряет все запросы - на самом деле он замедляет INSERT/UPDATE/DELETE.
  • Путаница между индексом и представлением (view) или материализованным представлением.
  • Отсутствие упоминания о необходимости проверки плана через EXPLAIN в реальной работе.

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

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