N+1 Queries: How to Fix Them

"Consultas N+1 são uma armadilha comum de desempenho ao usar Object-Relational Mappers (ORMs), como o Django ORM e o Prisma, em aplicações web. Este artigo explica o que são consultas N+1, por que elas afetam o desempenho e como corrigi-las na prática em bases de código que usam Django e Prisma.
O que é uma Consulta N+1?
O padrão de “consulta N+1” ocorre quando uma consulta inicial busca uma lista de itens, e em seguida uma nova consulta é executada para cada item a fim de buscar dados relacionados. Isso resulta em um total de N+1 consultas (N sendo o número de itens). Esse padrão ineficiente normalmente aparece quando ORMs carregam objetos relacionados de forma preguiçosa dentro de loops, o que gera várias idas desnecessárias ao banco de dados e torna a aplicação mais lenta.
Exemplo em Django:
for w in ProgramWorkout.objects.all():
print(w.id, w.program.id) # Isso dispara uma nova consulta para cada w.program (N+1 consultas)
Aqui, uma consulta busca todos os objetos ProgramWorkout, mas cada acesso ao programa relacionado gera outra consulta, totalizando N+1 consultas.
| Ruim | Bom |
| .all()
prefetch_related
: Executa uma consulta adicional para buscar todos os objetos relacionados de múltiplos itens principais e os une em memória Python. Ideal para relações reversas de ForeignKey e ManyToMany.ProgramWorkout.objects.prefetch_related('programworkoutExercise_set').all()
Use select_related
para relações de valor único (onde cada item tem apenas um objeto relacionado) e prefetch_related
para relações de múltiplos valores ou reversas (onde vários objetos compartilham itens relacionados).
Soluções no Prisma para Consultas N+1
Problemas de N+1 similares ocorrem no Prisma ao buscar dados relacionados em consultas separadas. O Prisma oferece diversas maneiras de corrigir isso:
- Use
include
para fazer carregamento antecipado (eager loading) dos campos relacionados em uma ou poucas consultas.const workouts = await prisma.programWorkout.findMany({ include: { program: true, programWorkoutExercise: true, }, });
- Ative
relationLoadStrategy: "join"
para realizar junções (joins) no banco de dados internamente, reduzindo o número de consultas para apenas uma. - O dataloader interno do Prisma Client agrupa múltiplas consultas
findUnique()
que ocorrem no mesmo ciclo do event loop em uma única consulta SQL, otimizando padrões de resolvers GraphQL.
Essas técnicas reduzem o tráfego de rede, a carga no banco de dados e melhoram a experiência do usuário, evitando consultas excessivas.
Depurando Consultas N+1
- Desenvolvedores Django podem usar o Django Debug Toolbar para inspecionar consultas SQL por endpoint e detectar padrões N+1.
- Ferramentas de monitoramento como o New Relic também podem identificar padrões ineficientes de chamadas ao banco e revelar um alto número de consultas por transação.
- Usuários do Prisma podem ativar o log de consultas ou usar ferramentas de profiling para detectar consultas excessivas e otimizá-las.
Boas Práticas
- Sempre analise seu código em busca de riscos de consultas N+1 ao acessar dados relacionados em loops.
- Use
select_related
ouprefetch_related
no Django, ouinclude
erelationLoadStrategy
no Prisma, conforme o tipo de relação do seu modelo de dados. - Faça o profiling regular de suas APIs com ferramentas de inspeção de consultas para manter o desempenho ideal.
Referências
- Prisma Docs: Otimização de Consultas e Desempenho
- [Documentação do Django ORM: select_related & prefetch_related]
- [Consultas N+1 no Django: Dicas e Soluções]"