Gymterview
senior

Как мониторить производительность PostgreSQL?

PostgreSQL предоставляет богатый набор системных представлений (pg_stat_*) и расширений для мониторинга. Шесть ключевых источников покрывают большинство задач диагностики.

1. pg_stat_statements — статистика запросов

Примеры запросов к pg_stat_statements
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- Топ-10 самых тяжёлых запросов по общему времени
SELECT
    queryid,
    substr(query, 1, 100) AS query,
    calls,
    round(total_exec_time::numeric, 2) AS total_time_ms,
    round(mean_exec_time::numeric, 2) AS avg_time_ms,
    rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

-- Запросы с наибольшим количеством чтений с диска
SELECT
    substr(query, 1, 80) AS query,
    calls, shared_blks_hit, shared_blks_read,
    round(100.0 * shared_blks_hit / NULLIF(shared_blks_hit + shared_blks_read, 0), 2) AS cache_hit_pct
FROM pg_stat_statements
ORDER BY shared_blks_read DESC
LIMIT 10;

2. pg_stat_user_tables — статистика по таблицам

Пример
SELECT
    relname AS table_name,
    seq_scan,            -- много Seq Scan — нужен индекс?
    idx_scan,            -- количество Index Scan
    n_tup_ins AS inserts, n_tup_upd AS updates, n_tup_del AS deletes,
    n_dead_tup AS dead_tuples,
    last_autovacuum
FROM pg_stat_user_tables
ORDER BY seq_scan DESC;

3. pg_stat_user_indexes — неиспользуемые индексы

Пример
SELECT
    schemaname, tablename, indexrelname,
    idx_scan,          -- 0 = не используется
    pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;

4. pg_stat_activity — текущие сессии и запросы

Пример
SELECT
    pid, usename, state,
    now() - query_start AS duration,
    wait_event_type, wait_event,
    substr(query, 1, 100) AS query
FROM pg_stat_activity
WHERE state = 'active' AND pid != pg_backend_pid()
ORDER BY duration DESC;

-- Завершить зависший запрос
SELECT pg_cancel_backend(pid);     -- мягкое (SIGINT)
SELECT pg_terminate_backend(pid);  -- жёсткое (SIGTERM)

5. Размер таблиц и индексов

Пример
SELECT
    relname AS table_name,
    pg_size_pretty(pg_total_relation_size(relid)) AS total_size,
    pg_size_pretty(pg_relation_size(relid)) AS table_size,
    pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) AS index_size
FROM pg_stat_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 10;

6. Cache hit ratio

Пример
SELECT
    sum(blks_hit) * 100.0 / NULLIF(sum(blks_hit) + sum(blks_read), 0) AS cache_hit_ratio
FROM pg_stat_database;
-- Хорошее значение: > 99%

На собеседовании: назовите три ключевых представления — pg_stat_statements (какие запросы тормозят), pg_stat_user_tables (много ли Seq Scan), pg_stat_activity (что выполняется сейчас). Cache hit ratio > 99% — признак здоровой системы, < 95% — нужно увеличить shared_buffers.