EXPLAIN, который я наконец-то понял
Долгое время EXPLAIN в Postgres был для меня
набором символов, в котором интересовало только число cost.
Решил наконец разобраться по-человечески.
EXPLAIN vs EXPLAIN ANALYZE
Первое — оценка плана без выполнения. Второе — реальный запуск с замером времени. На большой таблице ANALYZE может быть дорогим, но он показывает правду. Cost vs actual time — разные вещи; cost это абстрактные единицы планировщика.
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT * FROM events WHERE user_id = 42 ORDER BY created_at DESC LIMIT 10;
Что важно читать в выводе
- Тип узла: Seq Scan / Index Scan / Bitmap Heap Scan / Hash Join / Nested Loop. Если на большой таблице Seq Scan — обычно нужен индекс.
- rows в плане vs «actual rows». Большое
расхождение = устаревшая статистика, помогает
ANALYZE table. - BUFFERS: shared hit / read. Read = ходили на диск, hit = были в кэше. На прогретой системе hit-ы преобладают.
- Filter vs Index Cond: Filter применяется уже после чтения, Index Cond — частью обхода индекса. Условие в Filter с большой выборкой = потенциальное место для composite-индекса.
Один пример с моего проекта
Был запрос с WHERE status = 'active' AND created_at > ....
Индекс был только на status. План:
Bitmap Index Scan по статусу, потом Recheck
и фильтрация по дате. На 5 млн записей читал ~400К строк.
Сделал составной (status, created_at) — план
превратился в Index Range Scan, читал ~200 строк.
Время запроса упало с 600 мс до 4 мс.
Полезные настройки клиента
Если работаешь через psql:
\timing on
\pset format wrapped
SET enable_seqscan = off; -- временно, чтобы посмотреть, есть ли план без Seq Scan
Не магия. Просто внимательное чтение.