# Диагностика схемы PostgreSQL (EvoBGP) Runbook для оценки объёма БД и узких мест **перед** и **после** миграций оптимизации схемы. Выполнять на staging или production read-only сессией. ## Подключение ```bash psql "$EVOBGP_DATABASE_URL" ``` ## HTTP API (панель / мониторинг) При подключённом PostgreSQL control plane отдаёт instance-level метрики (роль **viewer+**): - `GET /v1/monitoring/postgres/overview` — подключения, TPS, cache hit, размер БД - `GET /v1/monitoring/postgres/queries` — top queries (`pg_stat_statements`, если extension включён) - `GET /v1/monitoring/postgres/locks`, `/tables`, `/recommendations` - `GET /v1/monitoring/correlation?window=60` — корреляция refresh jobs и cache hit Обслуживание (**operator**, async `202` + `job_id`): `POST /v1/postgres/vacuum`, `vacuum-analyze`, `analyze`, `reindex`, `cleanup`; журнал `GET /v1/postgres/maintenance/logs`. CLI на CP: `evobgp-api db report|vacuum|analyze|cleanup` (см. `internal/dbcli`). Миграция `000023` создаёт `pg_stat_statements`; для сбора статистики **обязательно** preload и перезапуск Postgres: ```text # postgresql.conf или command в compose shared_preload_libraries = 'pg_stat_statements' ``` После изменения — restart контейнера/сервиса Postgres. Без этого API `/v1/monitoring/postgres/queries` вернёт пустой список (`statements_available: false`), без 5xx. ## 1. Размеры таблиц и индексов ```sql SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS total, pg_size_pretty(pg_relation_size(relid)) AS heap, pg_size_pretty(pg_indexes_size(relid)) AS indexes FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC; ``` **Ожидание:** лидеры — `revision_materialized_prefix`, `config_revision` (TOAST от preview), JSONB-кэши. ## 2. Seq scan (горячие таблицы) ```sql SELECT schemaname, relname, seq_scan, seq_tup_read, idx_scan FROM pg_stat_user_tables WHERE schemaname = 'public' ORDER BY seq_tup_read DESC; ``` Сброс статистики после деплоя: `SELECT pg_stat_reset();` (только осознанно, теряется baseline). ## 3. Неиспользуемые индексы ```sql SELECT indexrelname, idx_scan, pg_size_pretty(pg_relation_size(indexrelid)) AS size FROM pg_stat_user_indexes WHERE schemaname = 'public' AND idx_scan = 0 ORDER BY pg_relation_size(indexrelid) DESC; ``` ## 4. Дубликаты в materialized prefixes Перед UNIQUE `(revision_id, prefix, community_id, source)`: ```sql SELECT revision_id, prefix, community_id, source, COUNT(*) AS n FROM revision_materialized_prefix GROUP BY 1, 2, 3, 4 HAVING COUNT(*) > 1 LIMIT 20; ``` ## 5. Шаблон отчёта staging | Метрика | До | После | Дата | |---------|-----|-------|------| | `revision_materialized_prefix` total | | | | | `config_revision` total | | | | | `module_prefix_snapshot` total | | | | | `asn_prefix_cache` total | | | | | Top seq_scan table | | | | | Unused indexes (count) | | | | ## 6. EXPLAIN для типовых запросов ```sql -- Список префиксов ревизии (keyset) EXPLAIN (ANALYZE, BUFFERS) SELECT prefix::text, community_id::text, source FROM revision_materialized_prefix WHERE revision_id = ''::uuid ORDER BY id LIMIT 51; -- Diff added (anti-join) EXPLAIN (ANALYZE, BUFFERS) SELECT b.prefix::text FROM revision_materialized_prefix b LEFT JOIN revision_materialized_prefix a ON a.revision_id = ''::uuid AND a.prefix = b.prefix WHERE b.revision_id = ''::uuid AND a.prefix IS NULL ORDER BY b.prefix LIMIT 5001; ``` Цель: Index Scan / Bitmap Index Scan по `(revision_id, …)`, без Seq Scan на больших таблицах.