> Как строить индексы в PostgreSQL (Node.js, JavaScript, PostgreSQL)

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

Компании: ЭНИРАН

Стек: Node.js, JavaScript, PostgreSQL

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

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

Индексы в PostgreSQL строятся командой CREATE INDEX с выбором типа (B-tree, Hash, GiST, GIN, BRIN) под конкретные запросы. Для Node.js важно учитывать покрывающие индексы (INCLUDE), частичные (WHERE) и композитные индексы для оптимизации JOIN и фильтрации. Используй EXPLAIN ANALYZE для проверки, избегай избыточности и следи за bloat через pg_stat_user_indexes.

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

PostgreSQL поддерживает несколько типов индексов, каждый для своих сценариев. B-tree - универсальный для равенства и диапазонов, Hash - только для равенства, GiST - для полнотекстового поиска и геоданных, GIN - для массивов и JSONB, BRIN - для больших таблиц с коррелированными данными. При построении индекса учитывай селективность: чем уникальнее значения, тем эффективнее. Композитные индексы работают по правилу leftmost prefix - порядок колонок в индексе должен совпадать с порядком условий в WHERE. Частичные индексы (WHERE status = 'active') экономят место и ускоряют запросы для подмножества данных. Покрывающие индексы (INCLUDE col) позволяют избежать чтения таблицы (index-only scan). В Node.js с PostgreSQL через pg или ORM (Sequelize, TypeORM) важно синхронизировать индексы с миграциями, используя CREATE INDEX CONCURRENTLY для production, чтобы не блокировать запись.

На практике

Для типичного backend на Node.js с PostgreSQL:

  • Для поиска по user_id и created_at в таблице заказов создай композитный B-tree: CREATE INDEX idx_orders_user_created ON orders (user_id, created_at DESC).
  • Для фильтрации по статусу и дате используй частичный индекс: CREATE INDEX idx_orders_active ON orders (created_at) WHERE status = 'active'.
  • Для JSONB полей (например, metadata->>'source') применяй GIN: CREATE INDEX idx_metadata_source ON events USING gin (metadata jsonb_path_ops).
  • В миграциях (например, через node-pg-migrate) используй CREATE INDEX CONCURRENTLY в отдельной транзакции, чтобы избежать блокировок на production.
  • Мониторь неиспользуемые индексы через pg_stat_user_indexes и удаляй лишние, чтобы не замедлять INSERT/UPDATE.

Пример кода

SQL
-- Композитный индекс для частого запроса
CREATE INDEX idx_users_email_status ON users (email, status);
-- Частичный индекс для активных пользователей
CREATE INDEX idx_users_active ON users (created_at) WHERE status = 'active';
-- Покрывающий индекс для index-only scan
CREATE INDEX idx_orders_total ON orders (user_id) INCLUDE (total_amount);
-- GIN для JSONB
CREATE INDEX idx_events_metadata ON events USING gin (metadata jsonb_path_ops);
-- Безопасное создание в production (в отдельной транзакции)
CREATE INDEX CONCURRENTLY idx_orders_date ON orders (created_at);

В Node.js с pg:

JAVASCRIPT
const { Pool } = require('pg');
const pool = new Pool();
async function createIndexes() {
const client = await pool.connect();
try {
await client.query('CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email ON users (email)');
} finally {
client.release();
}
}

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

Начни с краткого определения индекса и его роли в производительности. Затем перечисли основные типы и их применение, акцентируя на trade-off между скоростью чтения и замедлением записи. Приведи пример из практики: как выбирал тип индекса для конкретного запроса (например, GIN для JSONB в логах событий). Упомяни важность EXPLAIN ANALYZE для проверки и мониторинга bloat. Для senior-уровня добавь про CREATE INDEX CONCURRENTLY, частичные и покрывающие индексы, а также про влияние на ORM в Node.js. Заверши советом по профилированию: используй pg_stat_user_indexes и pg_stat_all_tables для выявления неиспользуемых индексов.

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

  • Понимание типов индексов и их сценариев (B-tree, GIN, BRIN и т.д.).
  • Умение проектировать композитные и частичные индексы под реальные запросы.
  • Знание trade-off: скорость чтения vs. замедление записи, размер индекса.
  • Опыт работы с production: создание индексов без блокировок (CONCURRENTLY), мониторинг.
  • Способность интегрировать индексы в Node.js-приложение (миграции, ORM).
  • Понимание планов запросов через EXPLAIN ANALYZE.

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

  • Создание индексов без анализа запросов (просто "на всякий случай").
  • Использование B-tree для JSONB или массивов вместо GIN/GiST.
  • Игнорирование порядка колонок в композитном индексе (нарушение leftmost prefix).
  • Создание индексов в production без CONCURRENTLY, что блокирует запись.
  • Отсутствие мониторинга: оставление неиспользуемых индексов, которые замедляют DML.
  • Переиндексация без учета bloat (используй REINDEX или VACUUM).
  • Слепое доверие ORM: автоматические индексы от Sequelize/TypeORM могут быть неоптимальными.

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

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