Skip to main content

O problema N+1 é um anti-pattern, conhecido por ser uma maneira ineficiente de realizar consultas numa base com um número relativamente grande de dados.

Neste artigo iremos explicar o comportamento desse problema, como identificá-lo e como corrigi-lo, mantendo o desempenho das suas aplicações eficiente.

Como ele se comporta?

Vamos imaginar que temos numa base de dados duas tabelas, uma de usuários e outra de artigos.

problema n+1
Base dedados com duas tabelas.

A tabela de usuários armazena as informações de cada usuário registrado no sistema e a tabela de artigos armazena as informações dos artigos de cada usuário. Logo temos uma relação de 1 para N, sendo o N o número de artigos que cada usuário escreveu.

Imagine que você precisa desenvolver um dashboard onde será exibido dados de todos os usuários e os títulos de todos os artigos de cada um deles. O que provavelmente você pensaria em fazer de primeira seria:

1°- Buscar pelas informações de todos os usuários:

SELECT ID, name, email FROM Users;

2°- Em seguida consultar o título de todos os artigos de cada usuário:

SELECT title FROM Articles WHERE user_id = id_usuario;

Agora pense que temos 500 usuários registrados na nossa base

As consultas serão executadas da seguinte maneira:

SELECT ID, name, email FROM Users;

SELECT title FROM Articles WHERE user_id = 1;
SELECT title FROM Articles WHERE user_id = 2;
SELECT title FROM Articles WHERE user_id = 3;
SELECT title FROM Articles WHERE user_id = 4;

SELECT titleFROM Articles WHERE user_id = 500;

O que podemos reparar é que no final executamos 501 consultas ao nosso banco de dados, por isso o nome N+1, sendo N o número de usuários.

Nesse exemplo acima temos poucas colunas nas nossas tabelas, mas muitas vezes, em sistemas empresariais de médio à grande porte, o número de colunas numa tabela pode ser bem maior. Isso faz com que o tempo que nossas consultas irão levar e o tempo de resposta da nossa aplicação aumente. O que afinal de contas, não é nada bom.

Como identificar o problema N+1?

Na grande maioria das vezes, quando temos uma consulta dentro de um loop temos o problema N+1. Trazendo um pouco mais para o dia a dia do desenvolvedor, abaixo deixo um pseudocódigo exemplificando este cenário:

n mais 1

Em seguida, abra o ambiente do Airflow clicando na opção Airflow

Como podemos notar, faremos uma primeira consulta que nos trás todos os usuários. Logo após,faremos N consultas, sendo N o número de usuários e para cada iremos trazer os artigos relacionados a ele.

Como resolver o problema N+1?

Agora vem aparte boa, vamos transformar este número de 501 consultas para apenas 2!

Primeiramente,iremos continuar fazendo a consulta que nos trás os usuários:

SELECT ID, name, email FROM Users;

Agora,iremos utilizar estas informações que temos para fazer a nossa segunda consulta. Nessa precisaremos antes de tudo iterar sobre nosso array de usuários para termos apenas um novo array com todos os IDs. Ficando da seguinte maneira :

SELECT title FROM Articles WHERE user_id IN (array_of_users_id);
// SELECT title FROM Articles WHERE user_id IN (1,2,3,4,…,500);

Alterando o pseudocódigo que montamos anteriormente, ele seguiria a seguinte lógica:

problema n1

Assim , oque fizemos de modificação foi apenas aproveitar os dados retornados dos usuários para criar um novo array, mas agora apenas com o ID de cada usuário, e realizar uma consulta no banco por todos os artigos que possuem relação com um dos IDs informados no operador IN. Totalizando apenas 2 consultas feitas ao banco.

Mas, e se usarmos JOIN?

Se você possui experiência com SQL, perceberá que podemos ao invés de usar o operador IN,realizar um JOIN e com isso apenas precisaríamos de 1 consulta.

O número de consultas pode afetar no desempenho da nossa aplicação, mas isso não significa que por ser 1 consulta será mais eficiente.

SELECT Users.ID, Users.nome, Articles.title FROM Users INNERJOIN Articles ON Articles.user_id = Users.ID;

Por exemplo,se cada usuário apenas tivesse um artigo escrito, o JOIN seria mais eficiente.Mas se cada usuário tiver em média 5 artigos, teremos muitos dados repetidos trafegando entre o banco de dados e a aplicação, o que pode consumir tempo e memória.

Conclusão

No final das contas, a modificação que fizemos para alterar o número de consultas na nossa base foi bem simples, mas muito poderosa.

Com ela tivemos um ganho de desempenho muito alto, deixando o tempo de resposta da nossa aplicação menor e consequentemente fazendo nosso cliente final muito mais feliz.

O objetivo desse artigo era te mostrar como boas análises feitas com calma, podem refletir de maneira significativa no comportamento do seu sistema.

Esperamos que tenha ajudado você a refletir sobre a maneira com que anda consumindo os dados da sua base e na maneira com que projeta suas aplicações.

Gostou do artigo? Confira agora o nosso artigo de introdução ao Interceptors NestJS!

Leave a Reply