4 Dicas para Resolver Problemas de N+1 Queries em Ruby on Rails
Ruby on Rails
Os problemas de N+1 queries em Ruby on Rails ocorrem quando sua aplicação faz um número excessivo de consultas ao banco de dados devido ao carregamento das associações entre modelos. Isso pode causar uma degradação significativa no desempenho, especialmente conforme o volume de dados aumenta.
Neste post, vamos abordar quatro maneiras eficazes de resolver o problema de N+1 queries em Rails, tornando sua aplicação mais rápida e eficiente.
1. Use includes
para Eager Loading
A solução mais comum para problemas de N+1 é o uso de includes
. Esse método permite carregar registros associados em uma única consulta, em vez de consultar o banco de dados cada vez que uma associação é acessada.
Sem includes
(Problema de N+1):
posts = Post.all
posts.each do |post|
post.comments.each do |comment|
puts comment.body
end
end
Neste exemplo, para cada post, o Rails executará uma consulta separada para buscar os comentários associados. Isso pode resultar rapidamente em dezenas ou até centenas de consultas à medida que o número de posts cresce.
Com includes
(Resolvendo N+1):
posts = Post.includes(:comments).all
posts.each do |post|
post.comments.each do |comment|
puts comment.body
end
end
Ao usar includes(:comments)
, o Rails carregará os posts e seus comentários associados em uma única consulta, evitando o problema de N+1.
2. Use joins
para Eficiência nas Consultas
Se você não precisa carregar os registros associados na memória e só precisa deles para filtros ou condições, o uso de joins
é uma solução mais eficiente. Enquanto includes
carrega os registros associados, joins
utiliza SQL para combinar os dados sem buscar todos os registros relacionados.
Exemplo:
Post.joins(:comments).where(comments: { approved: true })
Neste caso, joins
cria um INNER JOIN
em SQL entre as tabelas de posts e comentários, permitindo que você filtre os posts com comentários aprovados sem carregar todos os dados dos comentários na memória.
3. Considere preload
ou eager_load
em Casos Específicos
Tanto preload
quanto eager_load
são variações de eager loading no Rails, mas funcionam de maneira um pouco diferente de includes
.
preload
: Carrega os registros associados em consultas separadas, em vez de usar um join. Isso pode ser útil quando você não quer lidar com joins, mas ainda precisa evitar o problema de N+1.eager_load
: Força o Rails a usar umLEFT OUTER JOIN
para carregar tanto os registros primários quanto os associados em uma única consulta.
Quando usá-los:
- Use
preload
se você estiver lidando com um grande conjunto de dados e quiser evitar joins complexos. - Use
eager_load
quando quiser garantir que todas as associações sejam carregadas em uma única consulta.
Exemplo:
Post.preload(:comments).all # Consultas separadas para posts e comentários
Post.eager_load(:comments).all # Uma única consulta com LEFT OUTER JOIN
4. Selecione Apenas os Dados Necessários
Outra maneira de otimizar suas consultas é selecionar apenas as colunas que você precisa do banco de dados. Por padrão, o Rails carrega todas as colunas das tabelas associadas, mas muitas vezes você não precisa de todas elas.
Você pode usar select
para carregar apenas os dados relevantes:
posts = Post.select(:id, :title).includes(:comments).all
posts.each do |post|
puts post.title
end
Neste caso, em vez de carregar todas as colunas da tabela de posts, estamos selecionando apenas as colunas id
e title
, reduzindo a quantidade de dados sendo buscados no banco de dados.
Com essas quatro dicas, você pode melhorar significativamente o desempenho da sua aplicação Rails, evitando o problema de N+1 queries e tornando suas consultas ao banco de dados mais eficientes.