Pular para o conteúdo

PopSQL: suporte limitado.

Migrar agora →

Ferramenta grátis

Diagnóstico do seu Postgres em dois minutos, sem entregar a senha.

Rode uma consulta de leitura no seu banco e cole o resultado aqui. A página aponta índices nunca usados, tabelas com muita linha morta, transações paradas, sequências perto do limite e o uso de cache. A análise roda no seu navegador e nada é enviado.

01

Rode esta consulta no seu banco

Ela só lê as views de estatística do próprio Postgres (pg_stat_*, pg_catalog), não toca nos seus dados e funciona do Postgres 10 em diante, sem extensão. Use um usuário que enxergue as tabelas: o dono do banco ou alguém com o papel pg_monitor.

postgres-health.sql
-- DataRunner · Postgres health check (read-only, Postgres 10+)
SELECT json_build_object(
  'v', 1,
  'version', current_setting('server_version'),
  'database', current_database(),
  'size_bytes', pg_database_size(current_database()),
  'max_connections', current_setting('max_connections')::int,
  'stats_reset', (SELECT stats_reset FROM pg_stat_database WHERE datname = current_database()),
  'cache_hit_pct', (
    SELECT round(100.0 * sum(blks_hit) / nullif(sum(blks_hit) + sum(blks_read), 0), 2)
    FROM pg_stat_database WHERE datname = current_database()),
  'connections', (
    SELECT coalesce(json_object_agg(state, n), '{}')
    FROM (SELECT coalesce(state, 'other') AS state, count(*) AS n
          FROM pg_stat_activity WHERE backend_type = 'client backend' GROUP BY 1) c),
  'dead_tuples', (
    SELECT coalesce(json_agg(t), '[]') FROM (
      SELECT schemaname || '.' || relname AS name, n_live_tup AS live, n_dead_tup AS dead,
             greatest(last_autovacuum, last_vacuum) AS last_vacuum
      FROM pg_stat_user_tables WHERE n_dead_tup > 1000
      ORDER BY n_dead_tup DESC LIMIT 10) t),
  'unused_indexes', (
    SELECT coalesce(json_agg(t), '[]') FROM (
      SELECT s.schemaname || '.' || s.indexrelname AS name, s.relname AS "table",
             pg_relation_size(s.indexrelid) AS bytes
      FROM pg_stat_user_indexes s JOIN pg_index i ON i.indexrelid = s.indexrelid
      WHERE s.idx_scan = 0 AND NOT i.indisunique AND NOT i.indisprimary
      ORDER BY pg_relation_size(s.indexrelid) DESC LIMIT 10) t),
  'no_primary_key', (
    SELECT coalesce(json_agg(t), '[]') FROM (
      SELECT n.nspname || '.' || c.relname AS name, c.reltuples::bigint AS rows
      FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
      WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog', 'information_schema')
        AND n.nspname NOT LIKE 'pg_toast%'
        AND NOT EXISTS (SELECT 1 FROM pg_constraint k WHERE k.conrelid = c.oid AND k.contype = 'p')
      ORDER BY c.reltuples DESC LIMIT 10) t),
  'long_transactions', (
    SELECT coalesce(json_agg(t), '[]') FROM (
      SELECT pid, state, application_name AS app,
             extract(epoch FROM now() - xact_start)::int AS seconds
      FROM pg_stat_activity
      WHERE xact_start IS NOT NULL AND now() - xact_start > interval '5 minutes'
        AND backend_type = 'client backend' AND pid <> pg_backend_pid()
      ORDER BY xact_start LIMIT 10) t),
  'sequences', (
    SELECT coalesce(json_agg(t), '[]') FROM (
      SELECT schemaname || '.' || sequencename AS name, last_value, max_value,
             round(100.0 * last_value / max_value, 2) AS pct
      FROM pg_sequences
      WHERE last_value IS NOT NULL AND increment_by > 0 AND last_value::numeric / max_value > 0.5
      ORDER BY 4 DESC LIMIT 10) t),
  'largest_tables', (
    SELECT coalesce(json_agg(t), '[]') FROM (
      SELECT schemaname || '.' || relname AS name, pg_total_relation_size(relid) AS bytes
      FROM pg_statio_user_tables ORDER BY 2 DESC LIMIT 5) t)
) AS datarunner_health;

No terminal, salve a consulta num arquivo e rode: psql "$DATABASE_URL" -XAt -f postgres-health.sql

02

Cole o resultado

A análise acontece neste navegador. O resultado não sai daqui.

Isso muda toda semana.

O DataRunner roda estas verificações e outras (locks, as consultas mais caras, réplica atrasada) no horário que você escolher, e avisa por e-mail, Slack, Telegram ou WhatsApp quando algo passa do limite. O plano grátis não pede cartão.

Perguntas

Por que não pedir a senha do banco?
Porque ninguém deveria entregar a senha de produção para um site que acabou de conhecer. A consulta é de leitura, você vê o texto inteiro antes de rodar, e o resultado só tem nomes de tabelas, índices e contagens.
Os números estão zerados ou estranhos.
As estatísticas do Postgres recomeçam num reset manual (pg_stat_reset), numa queda do servidor ou num failover para uma réplica; um restart normal não as zera. Logo depois de um desses, "índice nunca usado" quer dizer "não usado desde então". Rode de novo depois de alguns dias de uso normal.
Posso apagar os índices que apareceram?
Confira antes se nenhuma réplica de leitura usa o índice (a estatística é por servidor) e se ele não sustenta uma restrição. Na dúvida, rode a consulta de novo daqui a uma semana.
Funciona em RDS, Cloud SQL, Supabase, Neon?
Sim, são todos Postgres. Em alguns serviços gerenciados, o usuário padrão não vê a atividade dos outros usuários; nesse caso a parte de conexões e transações vem incompleta.