System Design/03 - Qualidade/Performance Antipatterns2 min
Busy Database
- Que problema isso resolve?
- Quando usar / quando NÃO usar?
- Qual o principal trade-off?
- Como isso falha em produção?
Conceito
Empurrar trabalho para o banco que poderia ser feito em outro lugar — transformando o recurso mais caro e mais difícil de escalar do sistema em gargalo.
O banco é especial por três motivos: normalmente há um primário para escrita, escalá-lo custa muito mais que escalar aplicação, e ele é compartilhado por todos os componentes. Cada ciclo de CPU gasto ali é o ciclo mais caro do sistema.
Formas comuns
| Forma | Por que é ruim |
|---|---|
| Lógica de negócio em procedure | Difícil de testar, versionar e escalar |
| Trigger encadeada | Efeito invisível; uma escrita dispara cascata |
| Formatação e apresentação em SQL | CPU do banco fazendo trabalho de aplicação |
| Relatório pesado no primário | Compete com o tráfego transacional |
| Processamento de arquivo/XML/JSON no banco | O banco vira servidor de aplicação |
| Cron implementado como job do banco | Sem observabilidade nem controle |
Trade-offs
O antipadrão oposto existe e é igualmente real: fazer na aplicação o que o banco faz melhor. Agregação, filtro, ordenação e junção pertencem ao banco — trazê-los para a aplicação é Extraneous Fetching.
A linha divisória prática:
| Pertence ao banco | Pertence à aplicação |
|---|---|
| Filtrar, agregar, ordenar, juntar | Regra de negócio |
| Garantir integridade e unicidade | Formatação e apresentação |
| Transação | Orquestração entre serviços |
Há também um caso legítimo para procedures: operações que exigem várias idas e voltas dentro de uma transação, onde executar no banco elimina latência de rede sob lock. O custo é o de sempre — código fora do repositório principal, testado de forma diferente.
E a saída mais eficaz raramente é reescrever a lógica: é tirar a carga analítica do primário. Réplica de leitura para relatórios (Replication) resolve o sintoma mais comum sem mexer em nada.
Exemplo prático
Uma trigger que, a cada INSERT em pedidos, atualiza estoque, grava auditoria e recalcula o total do cliente.
Do lado da aplicação, é uma escrita simples. Do lado do banco, cada pedido custa quatro operações, segura locks por mais tempo e serializa sob concorrência. Na Black Friday, a contenção derruba o throughput de escrita — e nenhum log da aplicação explica por quê, porque a cascata é invisível a partir dela.
A alternativa é emitir um evento e deixar estoque, auditoria e agregados serem processados fora do caminho crítico (Event-Driven). A escrita volta a custar uma operação; a consistência entre os agregados passa a ser eventual — um trade-off explícito, em vez de um custo escondido.
Relacionado
Parte de Performance Antipatterns · roadmap.sh/system-design