Executando verificação de segurança...
2

Quando 200 ms viram 7 s: o pesadelo do N+1 e como eu o venci

Naquele dia, parecia que tudo estava sob controle: deploy estabilizado, métricas verdes, nenhum alerta no chat. Mas, de repente, começaram os feedbacks: “a página demora a carregar”, “o sistema está travando”… Aí percebi: aquilo não era bug, era performance — e com nome: o problema N+1.


Fiz minhas checagens e vi dezenas, centenas de queries pipocando em cada requisição. O sistema fazia uma consulta para carregar uma lista e, em seguida, mais uma consulta para cada item dela. Ou seja, 1 + N consultas. Isso não é teoria — é lógica cruel de produção. Como descreve Sergei Egorenkov: “Se você tem 100 estudantes, isso resulta em 101 queries (1 para estudantes + 100 para cursos)”


Esse não é um conceito reinventado na minha máquina — é um padrão conhecido, alertado por muitos. No StackOverflow, um dos colaboradores mais experientes resume bem: “é um anti-padrão de performance onde uma aplicação envia N+1 queries pequenas em vez de uma que busca todos os dados necessários”


Na prática, o que vi foi isso: em vez de buscar todos os posts e seus autores de uma vez, o sistema buscava os posts e, para cada um, disparava uma nova query para buscar o autor — mesmo já tendo carregado os posts. Localmente, com 20 registros, nada parecia. Em produção, com milhares, o banco travou — de 200ms para 7 segundos.


A solução? Simples de entender, mas exige disciplina: Eager Loading, ou “pré-carregamento” das relações necessárias. No exemplo do PlanetScale, foi demonstrado claramente: em vez de várias queries dentro do loop, você faz um JOIN e traz tudo de uma vez. O resultado? 10× mais rápido — 0,16 s versus 1,4 s


Tudo bem, mas nem sempre JOIN resolve tudo — às vezes, pode causar cartesian products ou resultados duplicados em hierarquias complexas. Por isso, muitas ORMs oferecem formas mais refinadas: “batch-size”, subselects, select_related() ou prefetch_related() em Django, join fetch em JPA/Hibernate, etc.


Além disso, percebi que muitos desses N+1 passam batidos nos logs normais, porque cada query individual é rápida. O problema é o acúmulo. É por isso que é essencial usar ferramentas de monitoramento e análise de queries, para identificar esses padrões antes que destrua a experiência do usuário.

Dicas que carreguei comigo desde então:

  • Nunca confie apenas nos testes locais. Volume de dados pode mudar tudo.
  • Fique de olho nas queries em produção — ORMs preguiçosos e loops mal planeados são armadilhas silenciosas.
  • Sempre que você ver um loop acessando relações em massa, pergunte: “Cadê meu Eager Loading nisso?”

O N+1 talvez não quebre seu código, mas compromete seu sistema. E a experiência do usuário — e da infraestrutura — paga o preço. Hoje, sempre que desenvolvo com ORMs, imagino esse cenário. E escrevo código que funcione e escale.

Se você ainda não se certificou de que seu código está livre desse vilão, comece agora. Ele pode estar funcionando… só não de forma eficiente.

Carregando publicação patrocinada...