← Назад к вопросам

Можно ли применить индекс для ускорения выполнения запроса?

2.0 Middle🔥 231 комментариев
#SQL и базы данных

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Индексы БД для ускорения запросов: как Product Analyst работает с инженерами

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

Что такое индекс и зачем он нужен

Аналогия: Без индекса — это как искать слово в словаре, читая каждую страницу подряд. С индексом — это как использовать оглавление.

Пример без индекса:

SELECT * FROM users WHERE email = 'john@example.com';

База проверяет КАЖДУЮ строку в таблице users (если 1M пользователей, проверяет 1M строк). Время: может быть 10-30 секунд.

С индексом:

CREATE INDEX idx_users_email ON users(email);
SELECT * FROM users WHERE email = 'john@example.com';

База использует индекс, находит за 1 операцию. Время: 1-10 миллисекунд.

Ускорение: в 1000-3000 раз!

Когда я добавляю индекс

Сценарий 1: Часто используется WHERE clause

-- Это запрос, который я запускаю каждый день
SELECT 
  COUNT(*) as total_orders,
  SUM(order_value) as total_gmv
FROM orders
WHERE user_id = 12345
  AND created_date >= '2026-01-01';

Если этот запрос работает медленно:

CREATE INDEX idx_orders_user_date ON orders(user_id, created_date);

Почему помогает: Индекс сортирует данные по user_id и дате, поэтому поиск очень быстрый.

Результат: Запрос спускается с 5 секунд на 100 миллисекунд.


Сценарий 2: Сортировка (ORDER BY)

-- Показать топ-10 продавцов по продажам
SELECT 
  seller_id,
  SUM(order_value) as total_sales,
  COUNT(*) as order_count
FROM orders
WHERE created_date >= '2026-01-01'
GROUP BY seller_id
ORDER BY total_sales DESC
LIMIT 10;

Без индекса БД должна:

  1. Прочитать ВСЕ строки из таблицы
  2. Отфильтровать по дате
  3. Сгруппировать
  4. Отсортировать
  5. Взять top 10

Это дорого. Но если есть индекс:

CREATE INDEX idx_orders_date ON orders(created_date);

Тогда БД может:

  1. Использовать индекс для быстрого доступа к строкам за дату
  2. Сгруппировать
  3. Отсортировать
  4. Взять top 10

Результат: Запрос спускается с 30 секунд на 1 секунду.


Сценарий 3: JOIN между таблицами

SELECT 
  u.user_id,
  u.email,
  COUNT(o.order_id) as order_count,
  SUM(o.order_value) as lifetime_value
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE u.country = 'USA'
GROUP BY u.user_id, u.email;

Для быстрого JOIN нужны индексы:

CREATE INDEX idx_users_country ON users(country);
CREATE INDEX idx_orders_user_id ON orders(user_id);

Почему: БД может быстро найти всех пользователей из USA, потом для каждого быстро найти их заказы.


Сценарий 4: IN clause (много значений)

SELECT *
FROM products
WHERE product_id IN (
  SELECT product_id FROM sales WHERE region = 'Europe'
);

Если есть индекс:

CREATE INDEX idx_sales_product ON sales(product_id);

Тогда БД может быстро найти все product_ids из Europe.

Как я работаю с инженерами на индексах

Шаг 1: Я пишу запрос

SELECT * FROM user_events WHERE user_id = 123 AND event_type = 'purchase';

Шаг 2: Я профилирую его (EXPLAIN)

EXPLAIN ANALYZE
SELECT * FROM user_events WHERE user_id = 123 AND event_type = 'purchase';

Результат EXPLAIN:

Seq Scan on user_events (cost=0.00..100000.00 rows=100000)
  Filter: (user_id = 123 AND event_type = 'purchase')
Planning Time: 0.1 ms
Execution Time: 5234.5 ms  ← ОЙ, МЕДЛЕННО!

Шаг 3: Я предлагаю индекс

CREATE INDEX idx_user_events_user_type ON user_events(user_id, event_type);

Шаг 4: Я даю инженеру результат

Э, посмотри, этот запрос работает 5+ секунд.
Если добавим индекс на (user_id, event_type), должно ускориться в 100x.
Пожалуйста, создай индекс.

Шаг 5: После добавления индекса

EXPLAIN ANALYZE
SELECT * FROM user_events WHERE user_id = 123 AND event_type = 'purchase';

Результат:

Index Scan using idx_user_events_user_type on user_events
  Index Cond: (user_id = 123 AND event_type = 'purchase')
Planning Time: 0.1 ms
Execution Time: 15.2 ms  ← ОТЛИЧНО! В 300x быстрее!

Типы индексов и когда их использовать

1. B-Tree Index (самый частый)

CREATE INDEX idx_name ON table(column);

Хорош для:

  • WHERE column = value
  • WHERE column > value
  • WHERE column IN (a, b, c)
  • ORDER BY column

Примеры:

CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_orders_date ON orders(created_date);

2. Hash Index

CREATE INDEX idx_hash ON table USING HASH (column);

Хорош для:

  • WHERE column = exact_value (очень быстро)

НЕ подходит для:

  • WHERE column > value
  • ORDER BY

3. Composite Index (несколько колонок)

CREATE INDEX idx_multi ON orders(user_id, status, created_date);

Хорош для запросов типа:

SELECT * FROM orders
WHERE user_id = 123
  AND status = 'completed'
  AND created_date > '2026-01-01';

Порядок колонок ВАЖЕН! Лучший порядок:

  • Equality условия первыми (user_id = 123)
  • Range условия в конце (created_date > ...)

4. Partial Index (условный)

CREATE INDEX idx_active_users ON users(email)
WHERE status = 'active';

Хорош когда:

  • Индекс нужен только для части таблицы
  • Экономит место

5. Full-Text Index (для поиска)

CREATE INDEX idx_search ON articles USING GIN (to_tsvector('english', content));

Для:

SELECT * FROM articles WHERE to_tsvector('english', content) @@ to_tsquery('database');

Когда НЕ нужен индекс

❌ Маленькая таблица

Если таблица имеет < 1000 строк, индекс может быть медленнее, чем full scan

❌ Редко используемый WHERE clause

Если запрос запускается один раз в месяц, индекс не стоит места

❌ Много UPDATE/INSERT (работает медленно)

Каждый раз когда вставляем новую строку, БД должна обновить индекс
Это добавляет overhead

❌ Низкая selectivity (много одинаковых значений)

CREATE INDEX idx_gender ON users(gender);

Этот индекс бесполезен, потому что у 50% юзеров gender = 'M', у 50% = 'F'. База говорит: "Зачем мне индекс, если всё равно нужно читать половину таблицы?"

Мой процесс оптимизации запросов

1. Определяю медленные запросы

-- PostgreSQL
SELECT 
  query,
  calls,
  total_time,
  mean_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;

2. Анализирую каждый (EXPLAIN)

EXPLAIN ANALYZE SELECT ...;

3. Вижу bottleneck:

  • Sequential Scan когда нужен Index Scan?
  • High cost?
  • Много rows scanned?

4. Предлагаю индекс (с коллегой инженера)

"Этот запрос может быть быстрее на 10x если добавим индекс"

5. Проверяю результат

EXPLAIN ANALYZE (после добавления)

6. Документирую:

Выигрыш: было 5 сек, стало 0.5 сек
Эффект: экономим X процессорных секунд в день
Потребление памяти: +50MB на сервере

Практический пример

Мой запрос (аналитика Retention):

SELECT 
  DATE_TRUNC('day', install_date) as cohort,
  DATE_TRUNC('day', return_date) - DATE_TRUNC('day', install_date) as days_since_install,
  COUNT(DISTINCT user_id) as returning_users
FROM user_cohorts
WHERE install_date >= '2026-01-01'
  AND return_date IS NOT NULL
GROUP BY DATE_TRUNC('day', install_date), DATE_TRUNC('day', return_date)
ORDER BY cohort, days_since_install;

Профилирую:

Execution Time: 45 seconds  ← УЖАС!

Анализирую EXPLAIN:

Seq Scan on user_cohorts (cost=0.00..500000.00 rows=5000000)
Filter: (install_date >= '2026-01-01' AND return_date IS NOT NULL)

Вижу проблему: читает все 5M строк, фильтрует по дате.

Решение:

CREATE INDEX idx_cohorts_install_return 
ON user_cohorts(install_date, return_date)
WHERE return_date IS NOT NULL;

После индекса:

Index Scan using idx_cohorts_install_return
Execution Time: 0.8 seconds  ← В 50x быстрее!

Вывод

Да, индексы ОЧЕНЬ помогают, но они не панацея. Как Product Analyst я:

  1. Идентифицирую медленные запросы (мониторю execution time)
  2. Анализирую с EXPLAIN (что БД делает?)
  3. Предлагаю индексы (работаю с инженерами)
  4. Измеряю эффект (benchmark до/после)
  5. Документирую (зачем индекс, какой выигрыш)

Это делает мою аналитику быстрой и масштабируемой, что улучшает мою способность отвечать на вопросы PM и инженеров в реальном времени.