Quando trabalhamos com o Entity Framework, frequentemente precisamos carregar dados relacionados entre tabelas. A facilidade de usar métodos como “Include” e “ThenInclude” para carregar essas relações é uma das vantagens do Entity Framework, mas também pode introduzir desafios de performance. Neste artigo vamos explorar como o recurso Query Splitting pode ajudar a otimizar consultas que envolvem múltiplas tabelas relacionadas.
O problema da "Cartesian Explosion"
Antes de mergulharmos na solução, é essencial entender o problema que o Query Splitting resolve. Imagine um cenário típico onde temos duas tabelas relacionadas: Blog e Post. Um blog pode ter muitos posts, representando um relacionamento um-para-muitos. Para carregar um blog junto com todos os seus posts, podemos usar o seguinte código:
var blogs = context.Blogs
.Include(b => b.Posts)
.ToList();
Por padrão, o Entity Framework gerará uma consulta SQL que usa um “JOIN” para unir as tabelas “Blogs” e “Posts”. Quando essas tabelas contêm muitos registros, especialmente em um relacionamento de muitos-para-muitos, o resultado pode ser uma combinação de todas as possíveis correspondências. Veja a consulta SQL gerada:
SELECT [b].[BlogId], [b].[Description], [b].[Title], [p].[PostId], [p].[BlogId], [p].[Content],
[p].[CreatedAt], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId]
Este fenômeno é conhecido como cartesian explosion, em que o número de linhas no resultado da consulta cresce exponencialmente, causando:
Desempenho degradado: a consulta pode se tornar muito lenta devido ao grande volume de dados sendo processado.
Uso excessivo de memória: o carregamento de um grande número de registros sobrecarrega a memória do servidor, impactando outras operações.
Aumento no tempo de resposta: o tempo para transportar os dados do banco para a aplicação aumenta, impactando a experiência do usuário.
Introduzindo o Query Splitting
O Query Splitting é uma técnica que ajuda a mitigar os problemas causados pela cartesian explosion. Em vez de executar uma única consulta complexa com múltiplos “JOINs”, o Entity Framework divide a consulta em várias consultas menores. Ao fazer isso, ele primeiro carrega os dados da entidade principal e, em seguida, carrega os dados relacionados usando consultas adicionais.
Imagine que queremos carregar todos os “Blogs” com seus respectivos “Posts”, mas queremos evitar o impacto de um grande “JOIN”. Podemos utilizar o método “AsSplitQuery” para dividir a consulta:
var blogs = context.Blogs
.Include(b => b.Posts)
.AsSplitQuery()
.ToList();
Quando utilizamos “AsSplitQuery()”, o Entity Framework executa as consultas de forma separada, resultando em duas consultas distintas:
SELECT [b].[BlogId], [b].[Description], [b].[Title]
FROM [Blogs] AS [b]
ORDER BY [b].[BlogId]
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[CreatedAt], [p].[Title], [b].[BlogId]
FROM [Blogs] AS [b]
INNER JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId]
Com “AsSplitQuery()”, o Entity Framework executa uma consulta para carregar todos os “Blogs” e, em seguida, uma consulta separada para cada conjunto de “Posts” relacionados. Essa abordagem reduz a quantidade de dados redundantes transferidos e evita a combinação excessiva de registros.
Vantagens do Query Splitting
Redução de dados redundantes: como cada consulta foca apenas em carregar os dados necessários para a entidade atual, há menos dados duplicados.
Melhora de performance em cenários específicos: para consultas onde a combinação de dados pode gerar muitas linhas (devido a relacionamentos muitos-para-muitos), dividir a consulta pode resultar em tempo de execução total menor.
Melhor uso da memória: consultas menores significam menos uso de memória no servidor, o que é ideal para aplicações com carga intensa ou ambientes com recursos limitados
Quando utilizar o Query Splitting?
Nem sempre o Query Splitting será a melhor solução. Ele é mais eficaz em cenários em que o problema da cartesian explosion está prestes a ocorrer ou já está causando problemas de performance. Se a consulta gerada normalmente retorna um número razoável de registros e a latência não é um problema, uma única consulta com “JOINs” pode ser mais eficiente.
No entanto, em situações em que você observa problemas de performance significativos devido a consultas com muitos “JOINs”, especialmente com tabelas grandes e relacionamentos complexos, o Query Splitting pode ser uma técnica de otimização valiosa.
Acelere a sua carreira conosco!
Se você é Desenvolvedor .NET Júnior e quer acelerar sua carreira até nível Pleno com salário de R$7k+, ou mesmo busca a primeira vaga, conheça a Mentoria .NET Start: Clique aqui
Se é Desenvolvedor .NET Pleno ou Sênior e quer virar referência técnica em sua equipe e mercado, com salário de R$10k+, conheça a Mentoria .NET Expert: Clique aqui
Conclusão
Em resumo, o Query Splitting não é uma solução mágica para todos os problemas de performance, mas uma ferramenta bastante útil no arsenal do desenvolvedor para cenários específicos onde o uso intensivo de” JOINs” está causando gargalos significativos.