Entity Framework è troppo lento. Quali sono le mie opzioni? [chiuso]


93

Ho seguito il mantra "Non ottimizzare prematuramente" e ho codificato il mio servizio WCF utilizzando Entity Framework.

Tuttavia, ho profilato le prestazioni e Entity Framework è troppo lento. (La mia app elabora 2 messaggi in circa 1,2 secondi, mentre l'app (legacy) che sto riscrivendo invia 5-6 messaggi nello stesso tempo (l'app legacy chiama sprocs per il suo DB Access).

La mia profilazione punta a Entity Framework che impiega la maggior parte del tempo per messaggio.

Allora, quali sono le mie opzioni?

  • Ci sono ORM migliori là fuori?
    (Qualcosa che supporta solo la normale lettura e scrittura di oggetti e lo fa velocemente ..)

  • C'è un modo per rendere più veloce Entity Framework?
    ( Nota : quando dico più veloce intendo a lungo termine, non la prima chiamata. (La prima chiamata è lenta (15 secondi per un messaggio), ma non è un problema. Ho solo bisogno che sia veloce per il resto dei messaggi.)

  • Qualche misteriosa terza opzione che mi aiuterà a ottenere più velocità dal mio servizio.

NOTA: la maggior parte delle interazioni con il database sono create e aggiornate. Faccio pochissimo selezionando ed eliminando.


Questo suona come un rimaneggiamento di "linq is slow" come fai a sapere che è EF? Hai profilato tutte le tue modifiche?
Maess

6
Alcune delle risposte puntano alle domande. Nella mia esperienza, la lentezza in EF ha poco a che fare con le query ma invece con i costi di materializzazione e tali costi sono spesso legati al rilevamento delle modifiche e al modo in cui ciò influisce sulle istanze create. Sfortunatamente, non ho una pallottola d'argento per te, quindi questo è solo un commento, ma consiglierei di vedere se la profilazione rivela alti costi di materializzazione e, in tal caso, di cercare cosa si può fare per tali costi.
Anthony Pegram

@Maess - Pensavo di aver indicato che avevo profilato e ho scoperto che era EF / DB che era lento. Ad ogni modo, sì, l'ho fatto. L'ho profilato e sono le interazioni EF / DB che sono il principale colpevole.
Vaccano

@Anthony - La materializzazione non è il primo genere di cose? Se è così, hai ragione che è molto lento. La prima corsa è super lenta. Ma come ho indicato, non sono troppo preoccupato per questo. È il rendimento totale che è il problema. (Se questo non è ciò che è Materializzazione, allora ho bisogno di fare qualche ricerca per vedere se è la causa del mio problema)
Vaccano

1
@Vaccano, no, la materializzazione è il processo di prendere i dati dal database e istanziare e popolare il grafico degli oggetti per rappresentare quei dati. Non sto parlando delle prestazioni alla prima esecuzione poiché il codice è jitted (o anche se Sql Server potrebbe creare il piano di esecuzione della query), ma cosa succede ogni volta che si ottengono dati sotto forma di oggetti.
Anthony Pegram

Risposte:


46

Dovresti iniziare profilando i comandi SQL effettivamente emessi da Entity Framework. A seconda della configurazione (POCO, entità Self-Tracking) c'è molto spazio per le ottimizzazioni. È possibile eseguire il debug dei comandi SQL (che non dovrebbe differire tra la modalità di debug e quella di rilascio) utilizzando il ObjectSet<T>.ToTraceString()metodo. Se incontri una query che richiede un'ulteriore ottimizzazione, puoi utilizzare alcune proiezioni per fornire a EF ulteriori informazioni su ciò che stai cercando di realizzare.

Esempio:

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 });
}

Potrebbe essere sostituito con:

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 });
}

L'ho appena digitato dalla mia testa, quindi non è esattamente come verrebbe eseguito, ma EF in realtà fa delle belle ottimizzazioni se gli dici tutto quello che sai sulla query (in questo caso, che avremo bisogno della categoria- nomi). Ma questo non è come il caricamento ansioso (db.Products.Include ("Categories")) perché le proiezioni possono ridurre ulteriormente la quantità di dati da caricare.


40
Questa risposta sembra ragionevole, finché non ti rendi conto che i tipi anonimi non sono accessibili al di fuori del metodo in cui sono definiti. Se vuoi caricare un oggetto complesso e non scrivere un megamoth, devi deserializzare i tuoi nuovi tipi anonimi in una sorta di POCO. Ancora una volta, sembra quasi ragionevole fino a quando non ti rendi conto che così facendo hai essenzialmente RISCRITTO IL TUO STRUMENTO DI ENTITÀ. Che è una stronzata.
Doug

5
ciò ha comportato un aumento di velocità 15x-20x per me.
Dave Cousineau

11
Risposta interessante e disponibile, valida ancora qualche tempo dopo. @Doug: Che non è davvero una cazzata dato che ottimizzi (usando le proiezioni) solo quelle poche query in cui hai davvero bisogno di usare il vantaggio extra. EF e POCO ti danno un valore predefinito ragionevole, il che è molto bello!
Victor

2
@Doug La maggior parte delle applicazioni ha modelli di visualizzazione per scenari di sola visualizzazione, giusto? Tanto vale fare la mappatura tanto quanto estrai i dati.
Casey

4
Mi sembra che gli ORM fossero il futuro. Avevano un senso, finché non ho iniziato a usarli. Poi ho trovato Dapper . Ora, quando vedo soluzioni come questa, rabbrividisco di come la complessità aumenta rapidamente. Scrivere SQL astratto in C # non è un modo per affrontare la vita.
Michael Silver

80

Il nocciolo della questione è che prodotti come Entity Framework saranno SEMPRE lenti e inefficienti, perché eseguono molto più codice.

Trovo anche sciocco che le persone suggeriscano di ottimizzare le query LINQ, guardare l'SQL generato, utilizzare debugger, precompilare, eseguire molti passaggi aggiuntivi, ecc. Cioè perdere molto tempo. Nessuno dice: Semplifica! Tutti vogliono complicare ulteriormente le cose facendo ancora più passi (perdere tempo).

Un approccio di buon senso sarebbe quello di non usare affatto EF o LINQ. Usa un semplice SQL. Non c'è niente di sbagliato in questo. Solo perché c'è una mentalità da branco tra i programmatori e sentono il bisogno di usare ogni singolo nuovo prodotto là fuori, non significa che sia buono o funzionerà. La maggior parte dei programmatori pensa che se incorporano ogni nuovo pezzo di codice rilasciato da una grande azienda, ciò li rende un programmatore più intelligente; non è affatto vero. La programmazione intelligente riguarda principalmente come fare di più con meno mal di testa, incertezze e nel minor tempo possibile. Ricorda: tempo! Questo è l'elemento più importante, quindi cerca di trovare modi per non sprecarlo risolvendo problemi in codice cattivo / gonfio scritto semplicemente per conformarsi a alcuni strani cosiddetti "schemi"

Rilassati, goditi la vita, prenditi una pausa dalla programmazione e smetti di usare funzionalità, codici, prodotti e "schemi" extra. La vita è breve e la vita del tuo codice è ancora più breve, e certamente non è scienza missilistica. Rimuovi livelli come LINQ, EF e altri e il tuo codice verrà eseguito in modo efficiente, scalerà e sì, sarà ancora facile da mantenere. Troppa astrazione è un cattivo "schema".

E questa è la soluzione al tuo problema.


155
Questo è buttare fuori il bambino con l'acqua sporca. Ottimizzi i colli di bottiglia, è sciocco buttare fuori EF perché è troppo lento in alcuni punti, mentre è molto veloce nella maggior parte degli altri. Perché non utilizzare entrambi? EF gestisce correttamente le stored procedure e l'SQL grezzo. Ho appena convertito una query LINQ-to-SQL che ha richiesto più di 10 secondi in un SP che richiede ~ 1 secondo, ma non ho intenzione di eliminare tutto LINQ-to-SQL. Ha risparmiato MOLTO tempo in altri casi più semplici, con meno codice e meno spazio per errori e le query sono verificate dal compilatore e corrispondono al database. Meno codice è una manutenzione più semplice e meno spazio per gli errori.
JulianR

11
Nel complesso, il tuo consiglio è buono, ma non credo sia giusto abbandonare EF o altre astrazioni perché non funzionano bene il 10% delle volte.
JulianR

49
Plain SQL = facile da mantenere? Non è vero per app di grandi dimensioni con molta logica di business. Scrivere SQL riutilizzabile complesso non è una cosa facile da fare. Personalmente ho avuto alcuni problemi di prestazioni con EF, ma questi problemi semplicemente non sono paragonabili ai vantaggi di un ORM adeguato in termini di RAD e mantenendo le cose ASCIUTTE (se è coinvolto un livello di complessità).
MemeDeveloper

13
+ 10 ^ 100 Troppa astrazione è un cattivo "schema"
Makach il

57
-1. "EF sarà SEMPRE lento e inefficiente". Non vedo perché affermi che qualcosa del genere è la verità assoluta. Avere più livelli da attraversare renderà qualcosa di più lento, ma se tale differenza è anche NOTICE dipende completamente dalla situazione come la quantità di dati e il tipo di query eseguita. Per me questa è la stessa cosa che dire "C # sarà SEMPRE lento e inefficiente" perché è un'astrazione più elevata del C ++. Eppure molte persone scelgono di usarlo perché la produttività aumenta di gran lunga la perdita di prestazioni (se ce n'è). Lo stesso vale per EF
Despertar

37

Un suggerimento è usare LINQ to Entity Framework solo per istruzioni CRUD a record singolo.

Per query, ricerche, rapporti e così via più complessi, scrivi una stored procedure e aggiungila al modello Entity Framework come descritto su MSDN .

Questo è l'approccio che ho adottato con un paio dei miei siti e sembra essere un buon compromesso tra produttività e prestazioni. Entity Framework non genererà sempre l'SQL più efficiente per l'attività in questione. E invece di spendere il tempo per capire perché, scrivere una stored procedure per le query più complesse in realtà mi fa risparmiare tempo. Una volta acquisita familiarità con il processo, non è una seccatura aggiungere processi archiviati al modello EF. E ovviamente il vantaggio di aggiungerlo al tuo modello è che ottieni tutta quella bontà fortemente tipizzata che deriva dall'uso di un ORM.


Hai un'idea dei metodi usati negli scaffolding come db.athlete.find (id) ecc. Come si comportano rispetto ad ADO.NET o dapper?
È una trappola il

15

Se stai semplicemente recuperando dati, è di grande aiuto per le prestazioni quando dici a EF di non tenere traccia delle entità che recupera. A tale scopo, utilizza MergeOption.NoTracking. EF genererà semplicemente la query, la eseguirà e deserializzerà i risultati negli oggetti, ma non tenterà di tenere traccia delle modifiche di entità o di qualsiasi altra cosa di quella natura. Se una query è semplice (non passa molto tempo ad aspettare che il database ritorni), ho scoperto che impostarla su NoTracking può raddoppiare le prestazioni della query.

Vedi questo articolo MSDN sull'enumerazione MergeOption:

Risoluzione dell'identità, gestione dello stato e rilevamento delle modifiche

Questo sembra essere un buon articolo sulle prestazioni EF:

Prestazioni e Entity Framework


9
Prima che qualcuno lo faccia, potrebbe essere una buona idea leggere qui. stackoverflow.com/questions/9259480/...
leen3o

6

Dici di aver profilato l'applicazione. Hai profilato anche tu l'ORM? C'è un profiler EF di Ayende che evidenzierà dove puoi ottimizzare il tuo codice EF. Potete trovare qui:

http://efprof.com/

Ricorda che puoi utilizzare un approccio SQL tradizionale insieme al tuo ORM se hai bisogno di migliorare le prestazioni.

Se esiste un ORM più veloce / migliore? A seconda del modello oggetto / dati, potresti prendere in considerazione l'utilizzo di uno dei micro-ORM, come Dapper , Massive o PetaPoco .

Il sito Dapper pubblica alcuni benchmark comparativi che ti daranno un'idea di come si confrontano con altri ORM. Ma vale la pena notare che i micro-ORM non supportano il ricco set di funzionalità degli ORM completi come EF e NH.

Potresti voler dare un'occhiata a RavenDB . Si tratta di un database non relazionale (di nuovo da Ayende) che consente di archiviare i POCO direttamente senza la mappatura necessaria . RavenDB è ottimizzato per le letture e semplifica notevolmente la vita degli sviluppatori eliminando la necessità di manipolare lo schema e di mappare i tuoi oggetti su quello schema. Tuttavia, tieni presente che questo è un approccio significativamente diverso rispetto all'utilizzo di un approccio ORM e questi sono descritti nel sito del prodotto .


3

Ho trovato la risposta di @Slauma qui molto utile per velocizzare le cose. Ho usato lo stesso tipo di pattern sia per gli inserti che per gli aggiornamenti e le prestazioni sono aumentate alle stelle.


2

Dalla mia esperienza, il problema non con EF, ma con ORM si avvicina a se stesso.

In generale, tutti gli ORM soffrono di problemi N + 1, query non ottimizzate e così via. La mia ipotesi migliore sarebbe quella di rintracciare le query che causano un degrado delle prestazioni e provare a mettere a punto lo strumento ORM o riscrivere quelle parti con SPROC.


1
La gente continua a dirmi questo. Ma imposterò una semplice istruzione di selezione usando la vecchia scuola ADO e la stessa semplice selezione usando un contesto EF ed EF è sempre notevolmente più lento. Mi piace davvero EF, ma continua a rendere la vita più difficile invece che più facile.
Sinaesthetic

1
@ Sinestetico Ovviamente è più lento. Allo stesso modo, il codice scritto usando Linq to Objects è solitamente più lento del codice scritto senza di esso. La domanda non è davvero se è più veloce o altrettanto veloce (come potrebbe essere, quando sotto il cofano deve ancora emettere la query che stavi emettendo a mano?) Ma se 1) è ancora abbastanza veloce per le tue esigenze 2) salva tempo scrivendo il codice 3) i benefici compensano i costi. Sulla base di questi elementi, penso che EF sia appropriato per molti progetti.
Casey

@ Sinestetico, aggiungerei anche che se non si utilizza un ORM, ciò che accade il più delle volte non è che ogni query SQL sia messa a punto e ottimizzata, ma che l'applicazione finisca per sviluppare un interno, organico, scarsamente ORM supportato e con prestazioni scadenti, a meno che la tua squadra non sia eccezionalmente disciplinata e molto preoccupata per le prestazioni.
Casey


1

Mi sono imbattuto anche in questo problema. Odio scaricare su EF perché funziona così bene, ma è solo lento. Nella maggior parte dei casi voglio solo trovare un record o aggiornare / inserire. Anche semplici operazioni come questa sono lente. Ho recuperato 1100 record da una tabella in un elenco e l'operazione ha richiesto 6 secondi con EF. Per me questo è troppo lungo, anche il risparmio richiede troppo tempo.

Ho finito per creare il mio ORM. Ho estratto gli stessi 1100 record da un database e il mio ORM ha impiegato 2 secondi, molto più velocemente di EF. Tutto con il mio ORM è quasi istantaneo. L'unica limitazione al momento è che funziona solo con MS SQL Server, ma potrebbe essere modificato per funzionare con altri come Oracle. Uso MS SQL Server per tutto adesso.

Se vuoi provare il mio ORM ecco il link e il sito web:

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

O se vuoi usare nugget:

PM> Install-Package OR-M_DataEntities

Anche la documentazione è disponibile


0

Ha senso solo ottimizzare dopo aver profilato. Se scopri che l'accesso al DB è lento, puoi convertirlo in stored procedure e mantenere EF. Se scopri che è l'EF stesso a essere lento, potresti dover passare a un ORM diverso o non utilizzare affatto un ORM.


0

Abbiamo un'applicazione simile (Wcf -> EF -> database) che esegue facilmente 120 richieste al secondo, quindi sono più che sicuro che EF non è il tuo problema qui, detto questo, ho visto importanti miglioramenti delle prestazioni con le query compilate.


Il 98% del mio codice è creare e aggiornare chiamate. Non so se questo faccia la differenza, ma è molto più lento di 120 al secondo.
Vaccano

sì, non sarebbe un'applicazione tipica, ti suggerirei di profilare la tua applicazione. per noi si legge principalmente ...
np-hard

0

Ho usato EF, LINQ to SQL e dapper. Dapper è il più veloce. Esempio: avevo bisogno di 1000 record principali con 4 record secondari ciascuno. Ho usato LINQ per sql, ci sono voluti circa 6 secondi. Sono quindi passato a dapper, ho recuperato 2 set di record dalla singola procedura memorizzata e per ogni record ho aggiunto i sub record. Tempo totale 1 secondo.

Anche la procedura memorizzata utilizzava funzioni di valore di tabella con applicazione incrociata, ho trovato che le funzioni di valore scalare erano molto lente.

Il mio consiglio è di usare EF o LINQ to SQL e per determinate situazioni passare a dapper.


-1

Entity Framework non dovrebbe causare gravi colli di bottiglia. È probabile che ci siano altre cause. Potresti provare a passare da EF a Linq2SQL, entrambi hanno funzionalità di confronto e il codice dovrebbe essere facile da convertire, ma in molti casi Linq2SQL è più veloce di EF.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.