Что такое `pg_stat_statements` в PostgreSQL?

SeniorPostgreSQL · Backend·Обновлено 7 августа 2026
Коротко
pg_stat_statements — расширение PostgreSQL, которое собирает агрегированную статистику выполнения SQL-запросов: количество вызовов, суммарное и среднее время, количество строк, блочный I/O. Это основной инструмент для поиска медленных и ресурсоёмких запросов.

Что такое pg_stat_statements

pg_stat_statements — стандартное расширение PostgreSQL (входит в contrib), которое накапливает статистику выполнения всех SQL-запросов, обработанных сервером. Каждый уникальный запрос идентифицируется по нормализованной форме (литералы заменяются на $1, $2, ...), что позволяет группировать вызовы одного и того же запроса с разными параметрами.

Подключение

Расширение требует явной загрузки на уровне сервера:

-- postgresql.conf
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.max = 10000       -- максимум уникальных запросов
pg_stat_statements.track = all       -- all | top | none
pg_stat_statements.track_utility = on -- DDL, COPY и т.д.

После перезапуска сервера расширение создаётся в нужной базе:

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

Ключевые метрики представления

Представление pg_stat_statements (PostgreSQL 14+) содержит следующие важные колонки:

Колонка Описание
query Нормализованный текст запроса
calls Число вызовов
total_exec_time Суммарное время выполнения (мс)
mean_exec_time Среднее время (мс)
rows Суммарно возвращено строк
shared_blks_hit Попадания в shared buffer
shared_blks_read Чтения с диска
temp_blks_written Временные блоки (сортировки, хеши)
wal_bytes Объём сгенерированного WAL

Практические запросы

Топ-10 запросов по суммарному времени

SELECT
  round(total_exec_time::numeric, 2) AS total_ms,
  calls,
  round(mean_exec_time::numeric, 2)  AS mean_ms,
  round(
    (100 * total_exec_time / sum(total_exec_time) OVER ())::numeric, 2
  ) AS pct,
  query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

Запросы с высоким cache miss ratio

SELECT
  query,
  shared_blks_hit,
  shared_blks_read,
  round(
    100.0 * shared_blks_read
    / NULLIF(shared_blks_hit + shared_blks_read, 0), 2
  ) AS miss_pct
FROM pg_stat_statements
WHERE shared_blks_hit + shared_blks_read > 1000
ORDER BY miss_pct DESC
LIMIT 10;

Сброс накопленной статистики

-- Полный сброс (суперпользователь)
SELECT pg_stat_statements_reset();

-- Сброс конкретного запроса (PostgreSQL 12+)
SELECT pg_stat_statements_reset(userid, dbid, queryid);

Важные нюансы для senior-уровня

  • pg_stat_statements.track = top — по умолчанию в некоторых дистрибутивах; не отслеживает запросы внутри функций. all включает вложенные.
  • queryid — нестабильный хеш: после pg_upgrade или изменения схемы может измениться, нельзя использовать как долгосрочный ключ.
  • Производительность: overhead минимален (~1–3%), но при max меньше реального числа уникальных запросов старые записи вытесняются — следите за pg_stat_statements_info.dealloc.
  • PostgreSQL 13 добавил разделение total_exec_time на total_plan_time и total_exec_time; в ранних версиях планирование не выделялось отдельно.

Что хочет услышать интервьюер

Кандидат знает, что это расширение (не встроенная функция) и требует `shared_preload_libraries` + перезапуска

Понимает нормализацию запросов — почему `SELECT $1` объединяет все вызовы с разными литералами

Умеет интерпретировать ключевые метрики: `total_exec_time`, `mean_exec_time`, `shared_blks_read` для диагностики

Знает про `pg_stat_statements_reset()` и когда его применять (baseline до/после деплоя)

Осведомлён об ограничениях: `max`-лимит на уникальные запросы, нестабильность `queryid`, разница `track=top` vs `track=all`

Пример: Включение расширения и базовая настройка

-- 1. Добавить в postgresql.conf, затем перезапустить сервер
-- shared_preload_libraries = 'pg_stat_statements'
-- pg_stat_statements.max = 10000
-- pg_stat_statements.track = all

-- 2. Создать расширение в нужной БД
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- 3. Проверить, что расширение работает
SELECT count(*) FROM pg_stat_statements;

Пример: Топ ресурсоёмких запросов

-- Самые «дорогие» запросы по суммарному времени выполнения
SELECT
  round(total_exec_time::numeric, 2)          AS total_ms,
  calls,
  round(mean_exec_time::numeric, 2)            AS mean_ms,
  round(
    100.0 * total_exec_time
    / sum(total_exec_time) OVER (), 2
  )                                            AS pct_of_total,
  left(query, 120)                             AS query_preview
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

Пример: Анализ cache miss и временных файлов

-- Запросы, которые часто промахиваются мимо shared buffer
-- или создают временные файлы (сортировки, хеш-джойны)
SELECT
  calls,
  round(
    100.0 * shared_blks_read
    / NULLIF(shared_blks_hit + shared_blks_read, 0), 2
  )                      AS disk_read_pct,
  temp_blks_written,      -- > 0 означает spill на диск
  left(query, 100)        AS query_preview
FROM pg_stat_statements
WHERE (shared_blks_hit + shared_blks_read) > 500
   OR temp_blks_written > 0
ORDER BY disk_read_pct DESC NULLS LAST
LIMIT 10;

Типичные ошибки

Считают, что расширение активно по умолчанию — забывают про `shared_preload_libraries` и перезапуск

Ищут самый медленный средний запрос (`mean_exec_time`) и игнорируют `total_exec_time` — редкий, но длинный запрос может не быть узким местом

Не учитывают параметр `track`: при `top` статистика внутри PL/pgSQL-функций не собирается

Путают `shared_blks_hit` (из shared buffer) и `shared_blks_read` (с диска/OS cache) — неправильно оценивают cache hit ratio

Используют `queryid` как постоянный идентификатор для исторических сравнений — он может измениться после `ANALYZE` или апгрейда

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее