# Индексы PostgreSQL на практике: где они помогают, а где вредят

Не каждое поле в WHERE нужно индексировать. Разбираем частые ошибки на реальных запросах из наших проектов.

*Рубрика: Разработка · Опубликовано: 9 июля 2026 г. · Автор: Александр Покидов*

Канонический URL: https://pokidov.dev/blog/postgres-indeksy-na-praktike

---

Индекс — это не бесплатное ускорение. Каждая запись в таблицу теперь пишет ещё и в индекс, а на диске индекс занимает место, сопоставимое с самой таблицей.

## Когда индекс действительно нужен

- Поле часто участвует в WHERE, JOIN или ORDER BY.
- Таблица достаточно большая, чтобы последовательное сканирование было заметно дороже.
- Селективность высокая — индекс по полю status с тремя значениями почти бесполезен.

explain.sql
```sql
EXPLAIN ANALYZE
SELECT * FROM posts
WHERE status = 'published'
  AND published_at <= now()
ORDER BY published_at DESC
LIMIT 12;
```

> Не добавляйте индекс, потому что «так надёжнее». Добавляйте, потому что EXPLAIN это подтвердил.
> — внутренний код-ревью гайд

| Тип индекса | Хорош для | Плох для |
| --- | --- | --- |
| B-tree | равенство, диапазоны, сортировка | полнотекстовый поиск |
| GIN | JSONB, массивы, полнотекст | частые обновления строки |
| Частичный | узкий фильтр (например, status) | запросы без этого условия |

---

> **Частая ошибка**
Композитный индекс (a, b) не используется для запросов только по b — порядок колонок в индексе имеет значение.

Итог простой: сначала EXPLAIN ANALYZE, потом индекс, потом снова EXPLAIN ANALYZE — чтобы убедиться, что план запроса действительно изменился.

## Ключевые выводы

- Индекс ускоряет чтение и замедляет запись — добавлять его «на всякий случай» нельзя.
- B-tree подходит для равенства и диапазонов, GIN — для JSONB и полнотекстового поиска.
- EXPLAIN ANALYZE — обязательный шаг перед добавлением индекса, а не после.
- Частичные индексы экономят место, если запросы всегда фильтруют по одному условию.
