Melhores Práticas do Postgres para Aplicações Web Modernas
A maioria dos desenvolvedores escolhe PostgreSQL porque é gratuito, confiável e testado em batalha. Mas escolhê-lo é a parte fácil. O verdadeiro desafio é usá-lo bem — projetar schemas que resistam a requisitos em mudança, escrever queries que permaneçam rápidas à medida que os dados crescem e gerenciar conexões de uma forma que não mate silenciosamente o desempenho da sua aplicação.
Este artigo cobre as melhores práticas do PostgreSQL para aplicações web que mais importam em produção.
Pontos-Chave
- Use colunas tipadas com constraints para campos que você filtra, ordena ou usa em joins, e reserve JSONB para dados em evolução ou não estruturados.
- Escolha índices deliberadamente — B-tree para queries de igualdade e intervalo, GIN para JSONB e busca de texto completo, e índices parciais para filtragem com escopo.
- Use um connection pooler como PgBouncer em modo de transação para prevenir esgotamento de conexões, especialmente em ambientes serverless.
- Faça migrações de schema aditivas e sem bloqueio, e sempre verifique o que sua ferramenta de migração gera antes de executá-la em produção.
- Habilite
pg_stat_statementspara identificar queries lentas antes que se tornem incidentes de produção.
Design de Schema: Comece Estruturado, Mantenha-se Flexível
Um bom design de schema PostgreSQL começa com uma separação clara entre o que você sabe que importa e o que pode importar depois.
Para campos que você vai filtrar, ordenar ou usar em joins — IDs de usuário, timestamps, flags de status, chaves estrangeiras — use colunas tipadas adequadas com constraints. Para todo o resto, uma coluna JSONB oferece a flexibilidade de um document store sem abandonar a integridade relacional.
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
metadata JSONB DEFAULT '{}'::jsonb
);
Esta abordagem híbrida é uma das funcionalidades modernas mais práticas do PostgreSQL disponíveis. Você obtém imposição de schema onde importa e flexibilidade onde os requisitos ainda estão evoluindo. Quando um campo JSONB começa a aparecer regularmente em cláusulas WHERE, promova-o para uma coluna real e adicione uma constraint. Essa migração é direta e vale a pena fazer.
Uma coisa a evitar: armazenar tudo em JSONB desde o início. Parece flexível até você precisar impor uma constraint NOT NULL ou adicionar uma chave estrangeira — e então você acaba reescrevendo metade do seu schema de qualquer forma.
Estratégias de Indexação do PostgreSQL Que Realmente Ajudam
Índices são onde a maioria das dicas de desempenho do PostgreSQL convergem. Os padrões são adequados para começar, mas cargas de trabalho de produção precisam de escolhas deliberadas.
Índices B-tree lidam com a maioria das queries de igualdade e intervalo. Em versões recentes do PostgreSQL (18+), o planejador pode realizar skip scans em índices B-tree de múltiplas colunas, o que significa que a ordem das colunas importa menos do que costumava — mas começar com sua coluna mais seletiva ainda é o padrão mais seguro.
Índices GIN são a escolha certa para colunas JSONB e busca de texto completo:
CREATE INDEX idx_user_metadata ON users USING GIN (metadata);
Isso torna queries como WHERE metadata @> '{"plan": "premium"}'::jsonb rápidas mesmo em tabelas grandes.
Índices parciais são subutilizados e frequentemente a melhor ferramenta para filtrar registros ativos ou recentes:
CREATE INDEX idx_active_users ON users (email) WHERE deleted_at IS NULL;
Evite indexar todas as colunas. Cada índice adiciona overhead de escrita e consome memória. Indexe o que suas queries realmente filtram, verifique com EXPLAIN ANALYZE e remova índices que não estão sendo usados.
Discover how at OpenReplay.com.
Gerenciamento de Conexões para Cargas de Trabalho Web
O modelo de conexão do PostgreSQL é stateful e relativamente caro de estabelecer. Cada conexão gera um processo dedicado no servidor. Em escala, centenas de conexões de curta duração de uma aplicação web vão degradar o desempenho notavelmente.
A solução é um connection pooler entre sua aplicação e o banco de dados. PgBouncer é a escolha padrão. No modo de transação, ele multiplexa muitas conexões da aplicação sobre um número muito menor de conexões reais ao banco de dados.
Isso importa especialmente em ambientes serverless (Lambda, Vercel, Cloudflare Workers) onde cada invocação de função pode tentar abrir uma nova conexão. Sem pooling, você vai atingir o limite max_connections do PostgreSQL sob tráfego moderado. Veja a documentação do PostgreSQL sobre configurações de conexão para detalhes.
Migrações Seguras em Produção
Mudanças de schema são onde as coisas dão errado. O padrão mais seguro é fazer mudanças aditivas primeiro: adicione uma nova coluna com um padrão, preencha os dados retroativamente, depois remova a coluna antiga em um deployment posterior.
Evite operações ALTER TABLE que bloqueiam a tabela sob carga. Adicionar uma coluna NOT NULL sem um padrão, por exemplo, reescreve a tabela inteira em versões mais antigas do PostgreSQL. No PostgreSQL 11+, adicionar uma coluna com um padrão não-volátil não requer mais uma reescrita de tabela — mas ainda vale a pena verificar o que sua ferramenta de migração realmente gera antes de executá-la em produção.
Sempre execute migrações dentro de transações quando possível, e teste caminhos de rollback antes de fazer o deploy.
Desempenho de Queries: Encontre Queries Lentas Primeiro
Não adivinhe problemas de desempenho. Habilite pg_stat_statements para rastrear estatísticas de execução de queries em toda sua carga de trabalho:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
Depois consulte-o para encontrar suas queries mais lentas ou mais chamadas:
SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;
Uma vez que você tenha um alvo, use EXPLAIN (ANALYZE, BUFFERS) para entender o que o planejador está fazendo e se um índice ajudaria.
Conclusão
PostgreSQL recompensa os desenvolvedores que levam design de schema, indexação e gerenciamento de conexões a sério desde o início. Estes não são tópicos avançados — são a linha de base para executar uma aplicação web em produção sem surpresas. Comece com colunas tipadas e constraints onde elas importam, adicione JSONB apenas onde a flexibilidade é genuinamente necessária, e indexe com base em padrões reais de queries em vez de suposições. Combine isso com um connection pooler e uma estratégia disciplinada de migração, e você evitará as armadilhas mais comuns que derrubam aplicações de produção.
Perguntas Frequentes
Promova um campo JSONB para sua própria coluna quando você regularmente filtrar, ordenar ou fazer join nele. Colunas dedicadas permitem adicionar constraints NOT NULL, chaves estrangeiras e índices B-tree padrão, tudo isso melhora a integridade dos dados e o desempenho de queries comparado a consultar caminhos JSONB aninhados.
O modo de transação é o padrão correto para a maioria das aplicações web porque retorna conexões ao pool após cada transação, maximizando o throughput. Use o modo de sessão apenas se sua aplicação depende de recursos de nível de sessão como advisory locks, LISTEN/NOTIFY, mudanças de configuração de sessão ou tabelas temporárias que devem persistir através de múltiplas transações.
No PostgreSQL 11 e posteriores, adicionar uma coluna com um padrão não-volátil é uma operação apenas de metadados e não reescreve a tabela. No entanto, adicionar uma coluna NOT NULL sem um padrão em versões mais antigas bloqueia e reescreve a tabela inteira. Sempre verifique qual SQL sua ferramenta de migração gera antes de executá-la em produção.
Não há um número fixo. Indexe apenas as colunas que suas queries realmente filtram, ordenam ou usam em joins. Cada índice adicional aumenta o overhead de escrita e uso de memória. Use pg_stat_user_indexes para identificar índices não utilizados e removê-los. Comece minimalista, monitore com EXPLAIN ANALYZE e adicione índices com base em padrões de queries observados.
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.