Короткий ответ
Нужно читать план выполнения «снизу вверх» (от самых глубоких узлов к верхним), сравнивать оценку числа строк (rows) с фактическим числом (actual rows) на каждом шаге и искать узел с наибольшим фактическим временем — это и есть основная точка замедления.
Как это работает подробнее
Как это работает: PostgreSQL выполняет план как дерево операций (сканирование таблиц, соединения, сортировки, агрегации); каждый узел в EXPLAIN ANALYZE показывает actual time (включает время всех дочерних узлов) и actual rows. Большое расхождение между оценкой (rows=... в плане) и фактическим числом строк часто говорит об устаревшей статистике таблицы.
Пример кода
PostgreSQL: EXPLAIN ANALYZE с буферами
EXPLAIN (ANALYZE, BUFFERS)
SELECT c.region, SUM(o.amount)
FROM orders o
JOIN customers c ON c.customer_id = o.customer_id
GROUP BY c.region;Что использовать на практике
- На практике: типичные «красные флаги» —
Seq Scanна крупной таблице там, где ожидался индексный доступ,Nested Loopс большим количеством итераций по внешней таблице без индекса на внутренней, а также отдельный заметный по времени узелSortилиHash, который может говорить о нехваткеwork_memи уходе сортировки на диск. - Проверка: Если узел
Hash Joinпоказываетactual time=5000..5200на фоне общего времени запроса 5300 мс — именно это соединение является основным узким местом. Проверка: после добавления индекса или увеличенияwork_memповторно снять план и сравнить время этого узла.
Подводные камни
- Частые ошибки: оценивают запрос по суммарному времени выполнения всего плана, не разбирая его по узлам, и пытаются «оптимизировать всё подряд»; не обновляют статистику таблицы (
ANALYZE table_name) перед диагностикой, из-за чего оптимизатор строит план на основе неактуальных данных о распределении значений.