Что можно увидеть в explain-плане запросов в Postgres
Что проверяют
Умеете ли вы читать план, а не просто знаете, что EXPLAIN существует. Часто дают реальный план и спрашивают, где тут проблема, а следом: чем EXPLAIN отличается от EXPLAIN ANALYZE.
Как отвечать
EXPLAIN показывает дерево узлов, выбранное планировщиком, с его оценками. EXPLAIN ANALYZE запрос реально выполняет и добавляет фактические цифры. Что искать в плане:
- способ доступа:
Seq Scan,Index Scan,Index Only Scan, связкаBitmap Index ScanиBitmap Heap Scan; - соединения:
Nested Loopхорош для малых выборок по индексу,Hash Joinдля крупных,Merge Joinдля уже отсортированных входов; - оценки:
cost=0.43..8.45это стартовая и полная стоимость в условных единицах, а не миллисекунды;rowsиwidthэто ожидаемое число строк и их средний размер; - факт:
actual time,rows,loops. Время и строки указаны на одну итерацию, их нужно умножать наloops; - признаки проблем:
Rows Removed by Filterв тысячах,Sort Method: external merge Disk(не хватилоwork_mem),Batchesбольше единицы у узлаHash; - буферы:
BUFFERSпоказываетshared hitиread, то есть сколько страниц пришло из кеша, а сколько с диска. В PostgreSQL 18 они выводятся сANALYZEавтоматически.
Главный навык: сравнить оценку rows с фактом. Расхождение в десятки раз означает устаревшую статистику, лечится это ANALYZE таблицы или CREATE STATISTICS для связанных колонок. Из Python план удобно снять через QuerySet.explain(analyze=True) в Django или выполнить EXPLAIN текстом через SQLAlchemy.
Где спотыкаются
Читают cost как время. Это относительная величина, она нужна для сравнения планов между собой.
Запускают EXPLAIN ANALYZE на DELETE или UPDATE в проде и забывают, что запрос реально выполнится. Такое делают внутри BEGIN и ROLLBACK.
Смотрят на время узла и не умножают на loops, поэтому не видят, что вложенный цикл отработал сто тысяч раз.