Разбор плана запроса PostgreSQL на живой таблице журналов
Пошаговый подход к чтению EXPLAIN ANALYZE на больших таблицах логов и подбору индексов без угадывания.
Когда таблица логов вырастает до сотен миллионов строк, привычное ощущение от EXPLAIN ANALYZE меняется: уже недостаточно увидеть Seq Scan и просто добавить индекс. Важно понять, как меняется стратегия планировщика под реальными фильтрами, сортировкой и ограничением по времени.
С чего начинать
Первый шаг — зафиксировать реальный запрос приложения, а не его упрощенную версию. Часто именно лишняя сортировка или неявное приведение типов становятся причиной потери индекса.
explain analyze
select created_at, level, message
from app_logs
where created_at >= now() - interval '1 day'
and service_name = 'integration-api'
and level in ('warning', 'error')
order by created_at desc
limit 200;На что смотреть в плане
Если в узле сортировки обрабатываются сотни тысяч строк ради двухсот результатов, лучше перейти на составной индекс (service_name, created_at desc) и добиться Index Scan с ранним ограничением набора.
create index concurrently idx_app_logs_service_created_at
on app_logs (service_name, created_at desc)
include (level, message);Дополнительный плюс дает секционирование по дате, если аналитика почти всегда ограничена коротким окном времени. Тогда планировщик даже не рассматривает старые секции.
Полезная привычка
Сохраняйте текст плана до и после изменения индексов. Это позволяет сравнивать не только итоговую скорость, но и изменение стоимости узлов, чтения буферов и объема сортировки.