Pourquoi la même requête Linq-to-SQL consum-t-elle beaucoup plus de time CPU sur le server de database pour un projet différent?

J'ai un projet .Net 4 (nommez-le "A") qui utilise Linq-to-SQL pour interroger une database et j'ai un autre projet .Net 4 (nommez-le "B") avec similiar mais pas le même code qui interroge la même database que "A".

Les deux projets:

  • sont des projets C # {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
  • utiliser les mêmes assemblys (version v4.0.30319, même dossier)
    • System.dll
    • System.Data.dll
    • System.Data.Linq.dll

Le DataContext généré automatiquement est spécifique à chaque projet mais instancié de la même manière:

  • même string de connection utilisant l'authentification SQL
  • les deux DataContext définissent leur CommandTimeout de la valeur par défaut à 60 secondes
  • toutes les autres options de configuration pour DataContext sont les valeurs par défaut

La façon dont la requête Linq est construite n'est pas exactement la même pour les projets, mais la requête Linq résultante est la même. L'instruction de sélection SQL (T-) générée est également la même! (suivi et vérifié les handles SQL sur le server DB)

Le server de database est:

  • Microsoft SQL Server Enterprise 2005 x64 (9.00.4035.00)
  • Système d'exploitation: Microsoft Server 2003 R2 SP2 x64

Si le time CPU surveillé (sur le server db) a été augmenté de manière drastique pour la requête du projet "A" et qu'une exception de timeout de command a été levée. (System.Data.SqlClient.SqlException: Timeout a expiré)

D'autre part la requête de "B" exécutée en quelques secondes (environ 3) . J'ai été capable de reproduire le comportement en appelant à nouveau le code de "A" avec les mêmes parameters (pas de modifications au code ou à la database) . "B" même exécuté en quelques secondes en même time "A" augmentait son time CPU.

Malheureusement, après qu'un collègue a créé les indices de nouveau je ne peux plus réprimander le comportement. Le même collègue a mentionné que la requête s'est déroulée rapidement "le mois dernier" (bien qu'aucun code n'ait changé depuis "le mois dernier" …).

J'ai débogué le code pour les deux projets – les deux instances de DataContext se ressemblaient. Le handle sql du process server db contient la même instruction SQL. Mais "A" a jeté une exception de timeout et "B" exécuté en quelques secondes – répétitif!

Pourquoi la même requête Linq-to-SQL consum-t-elle beaucoup plus de time CPU sur le server de database pour le projet "A" que pour "B"?

Pour être précis: Si la requête s'exécute "lentement" pour des raisons répétitives, comment la même requête peut-elle s'exécuter plus vite simplement parce qu'elle est appelée par un autre code Linq-to-SQL?

Peut-il y avoir des effets secondaires que je ne connais pas (encore)? Existe-t-il des valeurs d'instance de DataContext?

En passant: l'instruction SQL – via SSMS – utilise le même plan de requête à chaque exécution.

Par souci d'exhaustivité, j'ai lié un échantillon de:

  • les fragments de code C # du projet "B" (la partie SqlRequest.GetQuery se ressemble pour les deux projets)
  • le file SQL contient le schéma de database approprié
  • le plan d'exécution de database

S'il vous plaît gardez à l'esprit que je ne peux pas divulguer le schéma complet de db ni le code ni datatables réelles que j'interroge. (Les tables SQL ont d'autres colonnes à côté de celles nommées et le code C # est un peu plus complexe car la requête Linq est construite conditionnellement.)

Mise à jour – plus d'informations au moment de l'exécution

Certaines propriétés des deux instances DataContext :

 Log = null; Transaction = null; CommandTimeout = 60; Connection: System.Data.SqlClient.SqlConnection; 

La SqlConnection a été créée à partir d'une string de connection comme celle-ci (les deux cas):

 "Data Source=server;Initial Catalog=sourceDb;Persist Security Info=True;User ID=user;Password=password" 

SqlCommands explicites ne sont pas exécutés pour transmettre les options SET à la session de database. Aucun ne contient les options TVF SET embeddedes.

Vous devez exécuter une trace sur SQL Server au lieu de déboguer cela du côté C #. Cela vous montrera tout ce que A et B exécutent sur le server. Le plan d'exécution ne vous sert à rien parce que c'est précisément cela – juste un plan. Vous voulez voir les instructions exactes et leurs mesures de performance réelles .

Dans les rares cas où vous me disiez que les deux SELECT sont exactement les mêmes mais qu'elles ont des performances très différentes, je suis pratiquement certain qu'elles s'exécutent sous différents niveaux d'isolation des transactions . Une seule command SQL est une transaction implicite même si vous n'en créez pas explicitement.

Si, pour une raison quelconque, la trace ne le précise pas, vous devez publier les commands exécutées avec leurs mésortingques.

Remarque: l' exécution d'une trace entraîne des coûts supplémentaires en termes de performances. J'essaierai donc de limiter la durée ou de la faire fonctionner en période creuse, si possible.

Je pense que vous allez vérifier LazyLoadingEnabled = "true" dans votre projet "A" edmx-file.

Si LazyLoadingEnabled = "true": En cas de chargement différé, les objects associés (objects enfants) ne sont pas automatiquement chargés avec leur object parent tant qu'ils ne sont pas demandés. LINQ par défaut prend en charge le chargement paresseux.

IF LazyLoadingEnabled = "false": En cas de chargement rapide, les objects associés (objects enfants) sont automatiquement chargés avec leur object parent. Pour utiliser le chargement Eager, vous devez utiliser la méthode Include ().