2010-10-08 23 views
24

Come suggerisce il titolo, sto riscontrando un problema con la prima query su un database SQL Server utilizzando Entity Framework.Entity Framework - Prima query lenta

Ho provato a cercare una risposta su diversi siti ma nessuno sembra avere una soluzione.

Sto caricando un bel po 'di righe dal database incluse due relazioni 0-molti.

I test sono stati eseguiti in Visual Studio 2010 utilizzando il modello Entity Framework 4.0 e il generatore POCO (non c'è molta differenza nei tempi tra entità normali e oggetti POCO). Ho anche utilizzato il modello di viste T4 per precompilare le viste.

Il database era in SQL Server 2008.

Quello che vorrei davvero sapere è il motivo per cui la prima query è soo molto più lento rispetto a qualsiasi query secondarie.

Voglio anche sapere se è possibile fare qualcosa per aumentare la velocità della prima query fino al punto in cui è entro limiti accettabili.

Questa è una grande domanda e si può ottenere altre query che sono ancora più grande ed è comprensibile che essi possono essere un po 'lento, ma 30 secondi è troppo lento per l'utente di attendere, soprattutto quando gruppi di dati possono ottenere gli stessi dati molto più veloce.

Ho eseguito alcuni test di temporizzazione per cercare di scoprire dove si trova il problema e sono rimasto un po 'sorpreso nel vedere che sembra che SQL Server sia lento alla prima query.

Tempi è la seguente:

.NET Application Testing:

  • prima query: 29,6 secondi
  • seconda query: 3,2 secondi

SQL Profiler:

  • First Qu ery: 27 secondi
  • seconda query: 3,2 secondi

SQL Server Query Finestra

  • prima query: 8 secondi
  • seconda query: 4 secondi

Timings nell'applicazione è stata misurata con la classe Stopwatch. È stata misurata solo la query e è stato utilizzato per eseguire la query.

I tempi in SQL Server Profiler si riferiscono alle stesse query eseguite nell'applicazione che mostrano che l'applicazione utilizza solo circa 2,6 secondi per riempire i dati negli oggetti.

Gli ultimi 27 secondi vengono utilizzati per l'esecuzione della query su SQL Server.

Guardando la query secondaria i tempi sono gli stessi sia per l'applicazione che per il server SQL ma l'esecuzione della query è molto più veloce questa volta.

Posso capire perché l'applicazione non utilizza in qualsiasi momento perché non ci sono nuove righe che devono essere convertite in oggetti, ma perché la query è molto più veloce, mi sarei aspettato qualche secondo a causa dei piani di esecuzione ma non 24 secondi.

Solo per scopi di test, ho copiato l'SQL che Entity Framework genera e ha aperto una nuova finestra di query con una connessione separata ed eseguito la query al suo interno.

Come puoi vedere occorrono 8 secondi per la prima query e 4 secondi per il secondo.

Spero che qualcuno abbia qualche suggerimento.

ps. Mi scuso per il muro di testo :)

Modifica 19-10-2010:
Ho eseguito ieri un test che sembra supportare le righe restituite in modo sequenziale. Ciò significa che quando una riga viene restituita dal database viene immediatamente materializzata (se non esiste già nel contesto), quindi viene restituita la riga successiva e così via.

Ecco perché sembra che la query impieghi molto tempo sul server di database perché il tempo di materializzazione è incluso nei tempi di profiler di SQL Server.

Non credo che questo sia un caso di lettura di SQL Server dal disco rigido. La query lenta si verifica ogni volta che c'è una "prima query" in EF.

ex.

  1. Eseguire la prima query con EF, l'istruzione SQL è più lenta allora qualsiasi richiesta secondaria
  2. Smaltire contesto/repository
  3. Creare un nuovo contesto
  4. Eseguire la stessa query come prima (di nuovo la prima la query è lenta e quindi l'istruzione SQL)

È quasi come se l'EF invia alcune opzioni insieme alla prima query che rallenta il server.

Come per la compilazione di query, poiché ricordo che la query è stata compilata la prima volta che viene utilizzata il che significa che la prima query richiederebbe ancora più tempo per l'esecuzione.

Le query secondarie sarebbero più veloci ma la velocità sulle query secondarie non è il problema.

Ho eseguito anche un test in cui ho creato una query compilata come statica in modo che fosse compilata per tutti i contesti creati.

Ho quindi creato un contesto, ho eseguito la query, distrutto il contesto e ne ho creato uno nuovo ed ho eseguito la stessa query ancora una volta.

La differenza non era così grande, solo pochi secondi e la prima volta che ho eseguito la query ci sono voluti ancora tanto tempo senza pre-compilarlo.

Come per la generazione di viste, lo implementiamo già utilizzando i modelli T4.

La risposta è davvero che EF funziona solo se non si fa nulla se non le query più semplici che restituiscono solo una quantità relativamente piccola di dati?

risposta

4

Bene, lotti di cose possono rallentare una query di SQL Server la prima volta che viene eseguita. Molti di loro non impiegano più secondi, comunque.

... Tranne per gli accessi casuali del disco rigido. La prima volta che si esegue la query, SQL Server potrebbe dover leggere le pagine del database dalla memoria del disco rigido. La prossima volta che esegui la query, quelle pagine sono probabilmente in memoria.

Per quanto riguarda Entity Framework, la prima volta che si esegue una query deve essere compilata in SQL. È possibile utilizzare il tipo CompiledQuery per eseguire la precompilazione delle query di Entity Framework al fine di eseguire questo lavoro in anticipo, prima che l'utente finale debba attendere.

Su un modello molto grande, anche la generazione di visualizzazioni richiede un po 'di tempo. Puoi spostarlo per compilare il tempo, invece. Vedi this article per altri suggerimenti.

+0

Ci scusiamo per la risposta in ritardo. Non credo che questo sia un caso di lettura del server SQL dal disco fisso. La query lenta si verifica ogni volta che esiste una "prima query" in EF. Per esempio. 1) Esegui la prima query con EF, l'istruzione SQL è più lenta di qualsiasi query secondaria. 2) Disponi il contesto/repository. 3) Crea un nuovo contesto. 4) Esegui la stessa query di prima. (di nuovo la prima query è lenta e quindi l'istruzione SQL) È quasi come se l'EF invia alcune opzioni insieme alla prima query che rallenta il server. –

+0

Per quanto riguarda la compilazione delle query, credo che la query sia compilata la prima volta che viene utilizzata, ad esempio la prima query richiederebbe ancora più tempo per essere eseguita. Le query secondarie sarebbero più veloci ma la velocità sulle query secondarie non è il problema. Ho anche fatto un test in cui ho creato una query compilata statica in modo che fosse compilata per tutti i contesti che sono stati creati. Ho quindi creato un contesto, ho eseguito la query, ho eliminato il contesto, ne ho creato uno nuovo e ho eseguito nuovamente la query. La differenza non era così grande, solo pochi secondi, e la prima volta che ho eseguito la query ci sono voluti ancora tanto tempo senza doverla precompilare. –

+0

Come per Generazione vista, lo implementiamo già utilizzando i modelli T4. La risposta è davvero che EF funziona solo se non fai altro che le query più semplici che restituiscono solo una quantità relativamente piccola di dati? –

13

Abbiamo riscontrato lo stesso problema nell'EF 5.0 e ad oggi una ricerca su Google superficiale non rivela un'accelerazione sufficiente.

In base a questo collegamento, http://msdn.microsoft.com/en-us/library/cc853327(v=vs.100).aspx "Caricamento dei metadati" comporta un costo di tempo moderato, ma deve verificarsi solo una volta per AppDomain. Non ho trovato pre-compilazione come trucchi per caricare i meta-dati.

La soluzione che abbiamo implementato è eseguire una query secondaria sul contesto in un thread separato all'avvio dell'applicazione. Questo carica i meta-dati, richiede ancora molto tempo (18-19 secondi nel nostro caso), ma l'applicazione è reattiva durante il caricamento. Anche il primo carico effettivo non richiede tanto tempo.

Si noti che nel nostro contesto è possibile che l'utente trascorra 18-19 secondi nell'applicazione prima che sia necessario effettuare una chiamata EF in risposta alle proprie azioni. Ovviamente, se questo non è possibile nella vostra applicazione, questo lavoro potrebbe non fornire un notevole aumento di velocità.

0

Abbiamo lo stesso problema. È solo con il primo approccio del codice. Abbiamo circa 1500 POCO (+1500 file di mappatura POCO). La compilazione richiede circa 1-2 minuti. Il metodo Context.table.Add() richiede circa 3-4 minuti ma per il primo oggetto. È come un brutto scherzo senza soluzione da nessuna parte. Questi 3-4 minuti sono probabilmente una sorta di "trasformazione POCO" EF. Un core della CPU funziona al 100% e non c'è nulla da fare in SQL profiler.

L'utilizzo del primo approccio del database (generazione di file xml edmx) per le stesse 1500 tabelle funziona normalmente. È veloce come previsto.

Nessuna soluzione da nessuna parte fino ad ora. Forse l'EF6 risolverà questo problema.

+2

Questo è esattamente lo stesso scenario che ho riscontrato. Ma questa non è davvero una risposta. Sarebbe più appropriato comunicarlo attraverso un commento. – quakkels

+0

In EF6 ho la stessa situazione: il primo approccio al database è veloce, Poco è lento –

6

Ho avuto lo stesso problema. E ho usato un trucco per risolvere quel problema. Poiché il framework Entity impiega un po 'più di tempo per accedere alla prima volta e successivamente memorizza alcuni dei risultati per la prima volta al suo livello (anche il server SQL memorizza separatamente il risultato). Quindi, ho accesso al framework Entity su La mia applicazione in modo asincrono. Ha funzionato per me. E la mia applicazione è diventata più fluida.

Nella pagina Global.asax

protected void Application_Start() 
    { 

     Start(() => 
     { 
      using (EF.DMEntities context = new EF.DMEntities()) 
      { 
       context.DMUsers.FirstOrDefault(); 
      } 
     }); 
    } 
    private void Start(Action a) 
    { 
     a.BeginInvoke(null, null); 
    } 
+0

Dopo molte ore di ricerca, questo ha effettivamente accelerato le mie domande "fredde". –

+0

Fantastico, funziona per me! Grazie – AllmanTool

1

Ho appena siamo imbattuti in questo post sul mio di ricerca per migliorare EF tempo di avvio.Dal momento che non ha una risposta, aggiungerò le mie scoperte in modo che altre persone possano approfittarne se incappano anche in questo post.

Si prega di notare che sto usando EF 6, e la soluzione è applicabile solo per EF 6.

David Roth ha registrato un article che affronta il problema.

Mikael Eliasson ha riassunto molto bene nel suo answer a una domanda simile:

  1. Utilizzando un negozio modello di db cache
  2. Genera viste precompilati
  3. Genera pre-compilato versione del EntityFramework utilizzando n -gen per evitare jitting