PostgreSQL pg_stat_statements: найти медленные запросы
pg_stat_statements собирает статистику выполнения SQL-запросов: число вызовов, суммарное и среднее время, прочитанные строки. Запросы нормализуются, поэтому одинаковые обращения с разными литералами попадают в одну строку. Это основной инструмент для поиска запросов, которые нагружают PostgreSQL.
Включить расширение и посмотреть топ
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
SELECT
calls,
round(total_exec_time::numeric, 1) AS total_ms,
round(mean_exec_time::numeric, 2) AS mean_ms,
rows,
query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;Одного CREATE EXTENSION недостаточно: библиотека должна быть загружена через shared_preload_libraries = 'pg_stat_statements' в postgresql.conf, после чего нужен перезапуск сервера. Без этого CREATE EXTENSION завершится ошибкой о недоступной библиотеке. В Docker-образе PostgreSQL расширение уже включено в поставку.
Столбцы total_exec_time и mean_exec_time появились в PostgreSQL 13; в более старых версиях они называются total_time и mean_time. Время указано в миллисекундах. calls показывает количество выполнений, а rows — суммарное число возвращённых или затронутых строк.
Нормализация заменяет литералы на $1, поэтому запрос с WHERE id = 42 и WHERE id = 7 считается одним. Для сравнения конкретных планов используйте EXPLAIN (ANALYZE, BUFFERS) на реальном запросе. Статистика хранится в shared memory и сбрасывается при перезапуске сервера, поэтому снимайте показатели после прогрева и не делайте выводов по одной минуте.
Сбросить статистику после релиза
SELECT pg_stat_statements_reset();Сброс полезен после деплоя или изменения индексов, чтобы отделить новые запросы от старых. Не выполняйте его на общем сервере без предупреждения: коллеги потеряют накопленные данные. Ограничьте доступ к представлению: текст запросов может содержать имена таблиц и фрагменты условий, хотя литералы и скрыты.
Включайте track_io_timing = on, если нужны показатели чтения с диска, и track_planning = on для времени планирования. Сами по себе цифры не указывают на проблему: запрос с большим total_exec_time может быть редким, а с большим mean_exec_time — выполняться на каждом запросе приложения.
Документация: pg_stat_statements, monitoring stats.