Entity Framework é muito lento. Quais são minhas opções? [fechadas]

93

Eu segui o mantra "Não otimize prematuramente" e codifiquei meu serviço WCF usando o Entity Framework.

No entanto, fiz o perfil do desempenho e o Entity Framework é muito lento. (Meu aplicativo processa 2 mensagens em cerca de 1,2 segundos, em que o aplicativo (legado) que estou reescrevendo faz de 5 a 6 mensagens ao mesmo tempo. (O aplicativo legado chama sprocs para seu acesso ao banco de dados.)

Minha criação de perfil aponta para o Entity Framework levando a maior parte do tempo por mensagem.

Então, quais são minhas opções?

  • Existem ORMs melhores por aí?
    (Algo que apenas suporta a leitura normal e escrita de objetos e faz isso rápido ..)

  • Existe uma maneira de tornar o Entity Framework mais rápido?
    ( Observação : quando digo mais rápido, quero dizer a longo prazo, não a primeira chamada. (A primeira chamada é lenta (15 segundos para uma mensagem), mas isso não é um problema. Eu só preciso que seja rápido para o resto das mensagens.)

  • Alguma terceira opção misteriosa que me ajudará a obter mais velocidade do meu serviço.

NOTA: A maioria das minhas interações com o banco de dados são criar e atualizar. Eu faço muito pouco selecionando e excluindo.

Vaccano
fonte
Isso soa como uma repetição de 'linq is slow', como você sabe que é EF? Você traçou o perfil de todas as suas alterações?
Maess
6
Algumas das respostas apontam para as dúvidas. Na minha experiência, a lentidão no EF tem pouco a ver com as consultas, mas sim com os custos de materialização, e esses custos geralmente estão vinculados ao controle de alterações e como isso afeta as instâncias criadas. Infelizmente, não tenho uma solução mágica para você, então este é apenas um comentário, mas eu recomendaria ver se o perfil revela altos custos de materialização e, em caso afirmativo, pesquise o que pode ser feito sobre esses custos.
Anthony Pegram
@Maess - Pensei ter indicado que tinha feito o perfil e descobri que o EF / DB estava lento. De qualquer maneira, sim, eu fiz. Fiz o perfil dele e são as interações EF / DB que são as principais culpadas.
Vaccano
@Anthony - A materialização não é o primeiro tipo de coisa? Nesse caso, você está certo ao dizer que é muito lento. A primeira corrida é super lenta. Mas, como indiquei, não estou muito preocupado com isso. O problema é o rendimento total. (Se isso não é materialização, então preciso pesquisar para ver se é a causa do meu problema)
Vaccano 01 de
1
@Vaccano, não, materialização é o processo de pegar os dados do banco de dados e instanciar e popular o gráfico de objetos para representar esses dados. Não estou falando sobre o desempenho da primeira execução conforme o código é montado (ou mesmo como o Sql Server pode criar o plano de execução da consulta), mas o que acontece toda vez que você obtém dados na forma de objetos.
Anthony Pegram

Respostas:

46

Você deve começar criando o perfil dos comandos SQL realmente emitidos pelo Entity Framework. Dependendo da sua configuração (POCO, entidades de auto-rastreamento), há muito espaço para otimizações. Você pode depurar os comandos SQL (que não devem diferir entre o modo de depuração e o modo de liberação) usando o ObjectSet<T>.ToTraceString()método. Se você encontrar uma consulta que requer otimização adicional, você pode usar algumas projeções para fornecer à EF mais informações sobre o que você está tentando realizar.

Exemplo:

Product product = db.Products.SingleOrDefault(p => p.Id == 10);
// executes SELECT * FROM Products WHERE Id = 10

ProductDto dto = new ProductDto();
foreach (Category category in product.Categories)
// executes SELECT * FROM Categories WHERE ProductId = 10
{
    dto.Categories.Add(new CategoryDto { Name = category.Name });
}

Pode ser substituído por:

var query = from p in db.Products
            where p.Id == 10
            select new
            {
                p.Name,
                Categories = from c in p.Categories select c.Name
            };
ProductDto dto = new ProductDto();
foreach (var categoryName in query.Single().Categories)
// Executes SELECT p.Id, c.Name FROM Products as p, Categories as c WHERE p.Id = 10 AND p.Id = c.ProductId
{
    dto.Categories.Add(new CategoryDto { Name = categoryName });
}

Acabei de digitar isso da minha cabeça, então não é exatamente assim que seria executado, mas EF na verdade faz algumas ótimas otimizações se você disser tudo o que sabe sobre a consulta (neste caso, que precisaremos da categoria- nomes). Mas isso não é como o carregamento antecipado (db.Products.Include ("Categories")) porque as projeções podem reduzir ainda mais a quantidade de dados a serem carregados.

J. Tihon
fonte
39
Essa resposta parece razoável, até que você perceba que os tipos anônimos não são acessíveis fora do método no qual são definidos. Se quiser carregar um objeto complexo e não escrever um megamoth, você precisará desserializar seus novos tipos anônimos em algum tipo de POCO. Novamente, isso soa quase razoável até que você perceba que, ao fazer isso, essencialmente REESCRITO SUA PRÓPRIA ESTRUTURA DE ENTIDADE. O que é besteira.
Doug
5
isso resultou em um aumento de 15x-20x na velocidade para mim.
Dave Cousineau
11
Resposta interessante e útil, ainda válida algum tempo depois. @Doug: O que não é realmente besteira, já que você só otimiza (usando projeções) aquelas poucas consultas em que realmente precisa usar o benefício extra. EF e POCO fornecem um padrão razoável, o que é muito bom!
Victor
2
@Doug A maioria dos aplicativos possui modelos de visualização para cenários somente visualização, certo? Pode muito bem fazer tanto do mapeamento quanto você extrai os dados.
Casey de
4
Eu costumava sentir que os ORM eram o futuro. Eles simplesmente faziam sentido, até que comecei a usá-los. Então eu encontrei Dapper . Agora, ao ver soluções como essa, me encolho ao ver como a complexidade aumenta rapidamente. Escrever SQL abstraído em C # não é uma maneira de passar pela vida.
Michael Silver
80

O fato é que produtos como o Entity Framework SEMPRE serão lentos e ineficientes, pois estão executando muito mais código.

Também acho bobagem que as pessoas estejam sugerindo que se deve otimizar consultas LINQ, olhar para o SQL gerado, usar depuradores, pré-compilar, realizar muitas etapas extras, etc., ou seja, perder muito tempo. Ninguém diz - simplifique! Todo mundo quer complicar ainda mais as coisas dando ainda mais passos (perda de tempo).

Uma abordagem de bom senso seria não usar EF ou LINQ. Use SQL simples. Não há nada de errado com isso. Só porque existe uma mentalidade de rebanho entre os programadores e eles sentem o desejo de usar cada um dos novos produtos que existem, não significa que seja bom ou funcione. A maioria dos programadores pensa que se incorporar cada novo trecho de código lançado por uma grande empresa, isso os tornará um programador mais inteligente; não é verdade. A programação inteligente é principalmente sobre como fazer mais com menos dores de cabeça, incertezas e no mínimo de tempo. Tempo de lembrar! Esse é o elemento mais importante, então tente encontrar maneiras de não desperdiçá-lo na resolução de problemas em código ruim / inchado escrito simplesmente para se conformar com alguns chamados 'padrões' estranhos

Relaxe, aproveite a vida, faça uma pausa na codificação e pare de usar recursos extras, código, produtos, 'padrões'. A vida é curta e a vida do seu código é ainda mais curta, e certamente não é ciência de foguetes. Remova camadas como LINQ, EF e outras e seu código será executado com eficiência, escalonará e, sim, ainda será fácil de manter. Abstração demais é um 'padrão' ruim.

E essa é a solução para o seu problema.

Sean
fonte
155
Isso é jogar o bebê fora com a água do banho. Você otimiza gargalos, é bobagem descartar o EF porque ele é muito lento em alguns lugares, enquanto é muito rápido na maioria dos outros. Por que não usar os dois? EF lida muito bem com stored procedures e raw SQL. Acabei de converter uma consulta LINQ em SQL que levou mais de 10 segundos em um SP que leva cerca de 1 segundo, mas não vou descartar todo o LINQ em SQL. Isso economizou MUITO tempo em outros casos mais simples, com menos código e menos espaço para erros e as consultas são verificadas pelo compilador e correspondem ao banco de dados. Menos código é mais fácil de manutenção e menos espaço para erros.
JulianR
11
No geral, seu conselho é bom, mas não acho certo abandonar EF ou outras abstrações porque elas não funcionam bem 10% do tempo.
JulianR
49
SQL simples = fácil de manter? Isso não é verdade para aplicativos muito grandes com muita lógica de negócios. Escrever SQL reutilizável complexo não é uma coisa fácil de fazer. Pessoalmente, tive alguns problemas de desempenho com a EF, mas esses problemas simplesmente não se comparam aos benefícios de um ORM adequado em termos de um RAD e de manter as coisas SECAS (se houver algum nível de complexidade envolvido).
MemeDeveloper
13
+ 10 ^ 100 Abstração demais é um 'padrão' ruim
Makach
57
-1. "EF SEMPRE será lento e ineficiente." Não vejo por que você afirma que algo assim é a verdade absoluta. Ter mais camadas para percorrer tornará algo mais lento, mas se essa diferença é NOTICEABLE é completamente dependente da situação, como a quantidade de dados e o tipo de consulta sendo executada. Para mim, isso é a mesma coisa que dizer 'C # SEMPRE será lento e ineficiente' porque é uma abstração mais alta do que C ++. Mesmo assim, muitas pessoas optam por usá-lo porque os ganhos de produtividade superam em muito a perda de desempenho (se houver). O mesmo se aplica a EF
Despertar
37

Uma sugestão é usar LINQ to Entity Framework apenas para instruções CRUD de registro único.

Para consultas, pesquisas, relatórios, etc. mais envolvidos, escreva um procedimento armazenado e adicione-o ao modelo Entity Framework conforme descrito no MSDN .

Esta é a abordagem que usei com alguns dos meus sites e parece ser um bom meio-termo entre produtividade e desempenho. O Entity Framework nem sempre gerará o SQL mais eficiente para a tarefa em questão. E, em vez de perder tempo tentando descobrir o porquê, escrever um procedimento armazenado para as consultas mais complexas realmente economiza tempo para mim. Depois de familiarizar-se com o processo, não é muito complicado adicionar procs armazenados ao seu modelo EF. E, claro, o benefício de adicioná-lo ao seu modelo é que você obtém todas as vantagens fortemente tipadas que vêm de usar um ORM.

Steve Wortham
fonte
Você tem uma ideia sobre os métodos usados ​​em scaffolding como db.athlete.find (id) etc. Como eles funcionam em comparação com ADO.NET ou dapper ??
É uma armadilha de
15

Se você é puramente buscando dados, é uma grande ajuda para o desempenho quando você diz ao EF para não manter o controle das entidades que ele busca. Faça isso usando MergeOption.NoTracking. EF irá apenas gerar a consulta, executá-la e desserializar os resultados para os objetos, mas não tentará rastrear as mudanças de entidade ou qualquer coisa dessa natureza. Se uma consulta for simples (não gasta muito tempo esperando o retorno do banco de dados), descobri que configurá-la como NoTracking pode dobrar o desempenho da consulta.

Consulte este artigo do MSDN no enum MergeOption:

Resolução de identidade, gerenciamento de estado e controle de mudanças

Este parece ser um bom artigo sobre o desempenho da EF:

Desempenho e Estrutura da Entidade

JulianR
fonte
9
Antes que alguém faça isso, pode ser uma boa ideia ler aqui. stackoverflow.com/questions/9259480/…
leen3o
6

Você diz que traçou o perfil do aplicativo. Você também traçou o perfil do ORM? Existe um profiler EF de Ayende que irá destacar onde você pode otimizar seu código EF. Você pode encontrá-lo aqui:

http://efprof.com/

Lembre-se de que você pode usar uma abordagem SQL tradicional junto com seu ORM se precisar para obter desempenho.

Se houver um ORM mais rápido / melhor? Dependendo do seu modelo de objeto / dados, você pode considerar o uso de um dos micro-ORMs, como Dapper , Massive ou PetaPoco .

O site do Dapper publica alguns benchmarks comparativos que darão uma ideia de como eles se comparam a outros ORMs. Mas é importante notar que os micro-ORMs não suportam o rico conjunto de recursos dos ORMs completos, como EF e NH.

Você pode querer dar uma olhada em RavenDB . Este é um banco de dados não relacional (de Ayende novamente) que permite armazenar POCOs diretamente sem a necessidade de mapeamento . RavenDB é otimizado para leituras e torna a vida do desenvolvedor muito mais fácil, removendo a necessidade de manipular o esquema e mapear seus objetos para esse esquema. No entanto, esteja ciente de que esta é uma abordagem significativamente diferente do uso de uma abordagem ORM e estes são descritos no site do produto .

Sean Kearon
fonte
3

Achei a resposta de @Slauma aqui muito útil para acelerar as coisas. Usei o mesmo tipo de padrão para inserções e atualizações - e o desempenho disparou.

MemeDeveloper
fonte
2

Pela minha experiência, o problema não é com a EF, mas com o próprio ORM.

Em geral, todos os ORMs sofrem de problemas N + 1, consultas não otimizadas e etc. Meu melhor palpite seria rastrear as consultas que causam degradação de desempenho e tentar ajustar a ferramenta ORM ou reescrever essas partes com SPROC.

Valera Kolupaev
fonte
1
As pessoas continuam me dizendo isso. Mas vou configurar uma instrução select simples usando o ADO da velha escola, e o mesmo select simples usando um contexto EF e EF é sempre consideravelmente mais lento. Eu realmente quero gostar de EF, mas isso torna a vida mais difícil em vez de mais fácil.
Sinestésico de
1
@Sinaesthetic Claro que é mais lento. Da mesma forma, o código escrito usando Linq to Objects é geralmente mais lento do que o código escrito sem ele. A questão não é realmente se é mais rápido ou tão rápido (como poderia ser, quando por baixo do capô ainda precisa emitir a consulta que você estava emitindo manualmente?), Mas se 1) ainda é rápido o suficiente para suas necessidades 2) ele salva seu tempo escrevendo o código 3) os benefícios compensam os custos. Com base nesses itens, acho que EF é apropriado para muitos projetos.
Casey
@Sinaesthetic, eu também acrescentaria que, se você não usa um ORM, o que acontece mais frequentemente não é que cada consulta SQL seja ajustada e otimizada, mas que o aplicativo acaba desenvolvendo um sistema interno, orgânico, - ORM com suporte e baixo desempenho, a menos que sua equipe seja excepcionalmente disciplinada e muito preocupada com o desempenho.
Casey de
1

Eu encontrei esse problema também. Eu odeio abandonar o EF porque funciona tão bem, mas é lento. Na maioria dos casos, desejo apenas encontrar um registro ou atualizar / inserir. Mesmo operações simples como essa são lentas. Retirei 1100 registros de uma tabela em uma lista e essa operação levou 6 segundos com EF. Para mim, isso é muito longo, mesmo para salvar demora muito.

Acabei fazendo meu próprio ORM. Eu puxei os mesmos 1100 registros de um banco de dados e meu ORM levou 2 segundos, muito mais rápido do que EF. Tudo com meu ORM é quase instantâneo. A única limitação agora é que ele só funciona com o MS SQL Server, mas pode ser alterado para funcionar com outros como o Oracle. Eu uso o MS SQL Server para tudo agora.

Se você gostaria de experimentar meu ORM, aqui está o link e o site:

https://github.com/jdemeuse1204/OR-M-Data-Entities

Ou se você quiser usar o nugget:

PM> Instalar-Pacote OR-M_DataEntities

A documentação também está lá

TheMiddleMan
fonte
0

Só faz sentido otimizar depois de definir o perfil. Se você descobrir que o acesso ao banco de dados é lento, você pode converter para o uso de procedimentos armazenados e manter o EF. Se você descobrir que é o próprio EF que está lento, pode ser necessário alternar para um ORM diferente ou não usar um ORM.

Gabe
fonte
0

Temos um aplicativo semelhante (Wcf -> EF -> banco de dados) que faz 120 solicitações por segundo facilmente, então estou mais do que certo de que EF não é o seu problema aqui. Dito isso, tenho visto grandes melhorias de desempenho com consultas compiladas.

np-hard
fonte
98% do meu código são chamadas Criar e Atualizar. Não sei se isso faz diferença, mas é muito mais lento do que 120 por segundo.
Vaccano
sim, isso não seria um aplicativo típico, eu sugiro que você analise seu aplicativo. para nós é principalmente lido ...
np-hard
0

Usei EF, LINQ to SQL e dapper. Dapper é o mais rápido. Exemplo: eu precisava de 1000 registros principais com 4 sub-registros cada. Usei o LINQ to sql, demorou cerca de 6 segundos. Em seguida, mudei para dapper, recuperei 2 conjuntos de registros do único procedimento armazenado e, para cada registro, adicionei os sub-registros. Tempo total 1 segundo.

Além disso, o procedimento armazenado usou funções de valor de tabela com aplicação cruzada, descobri que as funções de valor escalar são muito lentas.

Meu conselho seria usar EF ou LINQ to SQL e, em certas situações, mudar para dapper.

tfa
fonte
-1

O Entity Framework não deve causar grandes gargalos. Provavelmente, existem outras causas. Você pode tentar mudar o EF para o Linq2SQL, ambos têm recursos de comparação e o código deve ser fácil de converter, mas em muitos casos o Linq2SQL é mais rápido do que o EF.

Wiktor Zychla
fonte