2011-08-04 5 views
4

J'ai obtenu une requête LINQ très simple:LINQ Requête incroyablement lente - pourquoi?

List<table> list = (from t in ctx.table 
        where 
        t.test == someString 
        && t.date >= dateStartInt 
        && t.date <= dateEndInt 
        select t).ToList<table>(); 

Le tableau qui obtient interrogé a obtenu environ 30 millions de lignes, mais les colonnes test et date sont indexées. Lorsqu'il faut retourner environ 5000 lignes, il faut plusieurs minutes pour terminer.

J'ai également vérifié la commande SQL générée par LINQ. Si j'exécute cette commande sur SQL Server, il faut 2 secondes pour terminer.

Quel est le problème avec LINQ ici? C'est juste une requête très simple sans aucune jointure.

C'est la requête SQL Profiler montre:

exec sp_executesql N'SELECT [t0].[test] 
FROM [dbo].[table] AS [t0] 
WHERE ([t0].[test] IN (@p0)) AND ([t0].[date] >= @p1) 
AND ([t0].[date] <= @p2)', 
N'@p0 nvarchar(12),@p1 int,@p2 int',@p0=N'123test',@p1=110801,@p2=110804 

EDIT:

Il est vraiment bizarre. Pendant les tests, j'ai remarqué que c'est beaucoup plus rapide maintenant. La requête LINQ prend maintenant 3 secondes pour environ 20000 lignes, ce qui est tout à fait correct.

Ce qui est encore plus déroutant: C'est le même comportement sur notre serveur de production. Il y a une heure c'était vraiment lent, maintenant c'est rapide encore. Comme je testais sur le serveur de développement, je n'ai rien changé sur le serveur de production. La seule chose que je peux penser d'être un problème est que les deux serveurs sont virtualisés et partagent le SAN avec beaucoup d'autres serveurs.

Comment puis-je savoir si c'est le problème?

+0

SQL Server en cache des requêtes, de sorte que vous ne pourriez pas effectuer un test équitable. –

+0

Avez-vous regardé le plan d'exécution? L'index est-il fragmenté? – Phill

+2

Regardez avec SQL Server Profiler pour savoir quelle requête est * vraiment * exécutée. – Heinzi

Répondre

2

Avant d'accuser LINQ, vérifiez d'abord où le délai réel a lieu.

  • Envoi de la requête
  • exécution de la requête
  • Recevoir les résultats
  • Transformer les résultats en types locaux
  • Reliure/montrant le résultat dans l'interface utilisateur
  • Et tout autre événement lié à ce processus

Puis commencer à blâmer LINQ;)

+0

Je voudrais savoir d'où vient le retard, mais je ne sais pas comment :) Comment puis-je définir un filtre dans SQL Server Profiler pour afficher simplement les actions sur une table spécifique? Il se passe trop de choses sur le serveur. – Dave

+0

@Dave, utilisez le profileur SQL pour voir quand et ce qui va vers et depuis SQL Server. Dénudez le client et utilisez la classe Stopwatch pour chronométrer les étapes. –

+0

@Dave, comment voulez-vous que je vous aide avec ce niveau d'information? Avez-vous connecté à la bonne base de données (utilisez-vous une copie locale?) Surveillez-vous les événements corrects? –

2

Si je devais deviner, je dirais « paramètre renifler » est susceptible, à savoir qu'il a construit et mis en cache un plan de requête basée sur un ensemble de paramètres, ce qui est très sous-optimale pour vos valeurs actuelles des paramètres. Vous pouvez aborder cela avec OPTION (OPTIMIZE FOR UNKNOWN) dans TSQL régulier, mais il n'y a pas de manière LINQ-to-SQL/EF de l'expulser.

Mon plan serait:

  1. profilage utiliser pour prouver que le temps est perdu dans la requête (par opposition à la matérialisation etc)
  2. une fois confirmé, pensez à utiliser des méthodes de TSQL directs pour invoquer

par exemple, avec LINQ to SQL, ctx.ExecuteQuery<YourType>(tsql, arg0, ...) peut être utilisé pour lancer TSQL brut au niveau du serveur (avec des paramètres comme {0} etc, comme string.Format). Personnellement, je pencherais plutôt vers "dapper" - utilisation très similaire, mais un matérialisateur plus rapide (mais il ne supporte pas EntityRef<> etc pour les valeurs de chargement paresseux - ce qui est généralement une mauvaise chose de toute façon car cela conduit à N + 1) .

dire (avec pimpant)

List<table> list = ctx.Query<table>(@" 
    select * from table 
    where test == @someString 
    and date >= @dateStartInt 
    and date <= @dateEndInt 
    OPTION (OPTIMIZE FOR UNKNOWN)", 
new {someString, dateStartInt, dateEndInt}).ToList(); 

ou (LINQ to SQL):

List<table> list = ctx.ExecuteQuery<table>(@" 
    select * from table 
    where test == {0} 
    and date >= {1} 
    and date <= {2} 
    OPTION (OPTIMIZE FOR UNKNOWN)", 
someString, dateStartInt, dateEndInt).ToList(); 
+0

Merci. J'ai essayé le second, mais ça prend le même temps. – Dave

+0

@Dave et vous le testez contre les * mêmes valeurs *? Ok ... quels sont les types DB ** complets ** de 'test' et' date'? par exemple, 'test' est-il typé' nvarchar (100) ',' char (20) ', etc? –

+0

Oui, c'est exactement pareil. J'ai fait quelques courses et j'ai comparé la durée avec StopWatch. test est "nvarchar (12)" et la date est "int". – Dave