PostgreSQL: EXPLAIN ANALYZE — как найти медленный запрос
Как включить и читать план выполнения запроса: EXPLAIN ANALYZE, BUFFERS, Seq Scan vs Index Scan. По официальной документации PostgreSQL.
Как использовать
- Перед своим запросом выполни: EXPLAIN (ANALYZE, BUFFERS) SELECT ...
- Seq Scan по большой таблице — добавь индекс по полям в WHERE/ORDER BY.
- Если rows и actual rows сильно расходятся — выполни ANALYZE table_name;
Поиск узких мест в запросах через план выполнения. По Using EXPLAIN и EXPLAIN.
EXPLAIN показывает план; EXPLAIN ANALYZE выполняет запрос и добавляет фактические время и число строк.
1. Базовый вызов
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE created_at > now() - interval '7 days';
- ANALYZE — запрос реально выполняется, в выводе появляются actual time, actual rows.
- BUFFERS — показывается использование shared/hit read (полезно для понимания кэша).
Форматы вывода: TEXT (по умолчанию), JSON, YAML, XML. Для консоли достаточно TEXT.
2. Как читать вывод
Плохо — последовательное сканирование большой таблицы:
Seq Scan on orders (cost=0.00..1234.00 rows=5000 width=80) (actual time=0.05..89.22 rows=4823 loops=1)
Filter: (created_at > (now() - '7 days'::interval))
Rows Removed by Filter: 95177
Planning Time: 0.12 ms
Execution Time: 91.45 ms
- Seq Scan — чтение всей таблицы.
- Rows Removed by Filter — сколько строк отброшено; при большом числе запрос тяжёлый.
Хорошо — использование индекса:
Index Scan using idx_orders_created_at on orders (cost=0.42..312.18 rows=5000 width=80) (actual time=0.03..8.11 rows=4823 loops=1)
Index Cond: (created_at > (now() - '7 days'::interval))
Planning Time: 0.08 ms
Execution Time: 9.02 ms
- Index Scan — планёр использует индекс; время выполнения обычно сильно меньше.
3. Что делать по результату
| Ситуация | Действие |
|---|---|
| Seq Scan по большой таблице | Добавить индекс по полям в WHERE / ORDER BY |
| rows в плане сильно ≠ actual rows | Выполнить ANALYZE table_name; |
| Нужна только статистика, без выполнения | Использовать EXPLAIN без ANALYZE |
Пример индекса по дате:
CREATE INDEX idx_orders_created_at ON orders(created_at);
После создания индекса снова выполни EXPLAIN (ANALYZE, BUFFERS) SELECT ... и сравни Execution Time.
4. Минимальный рабочий пример
-- Запрос, который хочешь проверить
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, total FROM orders WHERE status = 'paid' ORDER BY created_at DESC LIMIT 100;
-- Если видишь Seq Scan по orders — добавь индекс, например:
-- CREATE INDEX idx_orders_status_created ON orders(status, created_at DESC);
-- Затем снова EXPLAIN (ANALYZE, BUFFERS) ...
Документация: