“`html
Erros comuns em PostgreSQL que você deve evitar
Tem muita dor recorrente em PostgreSQL que não é “falta de sorte”: é padrão. Abaixo eu reuni erros que aparecem
em produção com frequência — e como eu evito cada um deles no meu dia a dia.
1 Consultas que ignoram índices (e viram “varredura” disfarçada)
O erro clássico: achar que “a query é simples” e só depois descobrir que ela varre tabelas enormes,
principalmente quando você usa filtros que não conseguem aproveitar índices.
Como isso aparece
Planos com Seq Scan onde você esperava Index Scan,
ou pior: combinações que resultam em Hash Join com entradas gigantes por falta de seletividade.
-
Funções em colunas dentro do filtro (ex.:
WHERE lower(email)=...) sem índice adequado. - Comparações com tipo diferente (ex.: comparar texto com UUID, ou timestamp com string), causando conversões.
-
LIKE com início livre (
LIKE '%abc') praticamente sempre elimina o ganho de índices B-Tree. - Falta de índice composto na ordem certa para o padrão de filtros e ordenação.
EXPLAIN (ANALYZE, BUFFERS).Se o plano não reflete o que você intuiu, ajuste o filtro/modelo/índice — não “a query no escuro”.
2 Modelar sem pensar em integridade (e depois “consertar” com lógica)
Eu vejo bastante esquema em que as colunas permitem estados inválidos e a aplicação tenta “corrigir”.
Isso causa dados inconsistentes, reprocessos e queries cada vez mais complexas.
- Ausência de chaves estrangeiras quando o relacionamento é real (N:1, 1:1, etc.).
- Falta de UNIQUE para regras de negócio (ex.: e-mail único, código único, slug único).
- Campos aceitando qualquer valor sem CHECK (ex.: status fora do conjunto permitido).
- Colunas nullable por padrão, mesmo quando o domínio exige valor sempre presente.
Por que isso custa caro
Quando você perde integridade no banco, você perde previsibilidade: joins podem retornar linhas “impossíveis”
e índices ficam menos eficientes porque o volume de lixo cresce com o tempo.
ela pertence ao modelo (UNIQUE/FK/CHECK/NOT NULL), não apenas ao código.
3 Ciclo de escrita e atualização ignorando VACUUM/UPDATES (bloat e lentidão progressiva)
Outro erro frequente: “funcionou ontem”. Com o tempo, o desempenho degrada por acúmulo de tuplas mortas.
Em PostgreSQL, isso não é surpresa — é consequência.
- Atualizações frequentes em registros grandes (muitas colunas), gerando mais churn de página.
- Esquecer índices desnecessários: cada INSERT/UPDATE paga o custo de manter todos os índices.
- Planos de manutenção inexistentes ou VACUUM atrasado para o volume real.
- Transações longas mantendo snapshots antigos (impedindo a reciclagem de tuplas).
Sinais de alerta
Aumento contínuo de bloat, piora de tempo de resposta sem mudança de query,
e crescimento do tamanho do banco sem justificativa.
Otimizar query é importante — mas se o “sistema de escrita” está criando lixo, você vai lutar sempre contra isso.
-- Exemplo de checagem rápida de dead tuples e sinais de bloat:
SELECT
schemaname,
relname,
n_live_tup,
n_dead_tup,
last_vacuum,
last_autovacuum,
vacuum_count,
autovacuum_count
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;
4 Transações frágeis e concorrência sem controle (locks, timeouts e inconsistência)
Erros de concorrência costumam aparecer tarde: em baixa carga tudo passa.
Em pico, aparecem deadlocks, esperas longas e filas de requisições.
- Transações longas com lógica demorada (chamadas externas, processamento grande, etc.).
- Ordem de atualização inconsistente entre transações (clássico para deadlock).
- Falta de estratégia de isolamento: assumir que “default é suficiente” para qualquer cenário.
- Atualizar sem precisar (UPDATE que muda valores idênticos), amplificando bloat e locks.
- Não tratar conflitos (ex.: UPSERT sem considerar concorrência e constraints).
Como eu evito deadlock e contenção
Eu desenho o fluxo para reduzir tempo de transação, manter a ordem de acesso consistente e usar
constraints para coordenar concorrência (em vez de “adivinhar” com lógica de aplicação).
reduzir a janela (CTE, UPSERT com constraint apropriada, ou SELECT…FOR UPDATE quando fizer sentido).
-- Exemplo de UPSERT usando constraint (evita duplicidade sob concorrência)
-- e mantém a regra no banco.
INSERT INTO usuarios (id, email, status)
VALUES ($1, $2, $3)
ON CONFLICT (email) DO UPDATE
SET
status = EXCLUDED.status,
updated_at = now();
Quer continuar evoluindo com PostgreSQL?
Se você curte o lado prático (modelagem, performance, planos e manutenção), eu recomendo ler os próximos posts:
eles complementam exatamente os pontos que mais quebram sistemas em produção.
“`
Sou Apaixonado pela programação e estou trilhando o caminho de ter cada diz mais conhecimento e trazer toda minha experiência vinda do Design para a programação resultando em layouts incríveis e idéias inovadoras! Conecte-se Comigo!