web-dev-qa-db-fra.com

Procédure stockée lente lorsque appelée depuis le Web, rapide depuis Management Studio

J'ai une procédure stockée qui expire incroyablement chaque fois qu'il est appelé à partir de l'application Web.

J'ai mis le feu à Sql Profiler, suivi les appels qui ont expiré et enfin découvert ces choses:

  1. Lors de l'exécution des instructions à partir de MS SQL Management Studio, avec les mêmes arguments (en fait, j'ai copié l'appel de procédure à partir de trace de profil SQL et je l'ai exécuté): Il se termine dans un délai de 5 à 6 secondes.
  2. Mais lorsque vous appelez depuis une application Web, cela prend plus de 30 secondes (en trace), donc ma page Web expire à ce moment-là.

Mis à part le fait que mon application Web a son propre utilisateur, tout est identique (même base de données, même connexion, serveur, etc.). J'ai aussi essayé d'exécuter la requête directement dans le studio avec l'utilisateur de l'application Web et cela ne prend pas plus de 6 seconde.

Comment puis-je savoir ce qui se passe?

Je suppose que cela n'a rien à voir avec le fait que nous utilisons des couches BLL> DAL ou des adaptateurs de table car la trace montre clairement que le délai est dans la procédure réelle. C'est tout ce à quoi je peux penser.

[~ # ~] modifier [~ # ~] J'ai découvert dans ce lien que ADO.NET définit ARITHABORT à vrai - ce qui est bon pour la plupart du temps, mais parfois, cela se produit, et la solution suggérée consiste à ajouter with recompile option pour le proc stocké. Dans mon cas, cela ne fonctionne pas mais je soupçonne que c'est quelque chose de très similaire à ceci. Quelqu'un sait ce que ADO.NET fait ou où je peux trouver la spécification?

92
iamserious

J'ai eu un problème similaire dans le passé, donc je suis impatient de voir une résolution à cette question. Le commentaire d'Aaron Bertrand sur le PO a mené à la requête a expiré lorsqu'elle est exécutée à partir du Web, mais très rapide lorsqu'elle est exécutée à partir de SSMS , et bien que la question ne soit pas un doublon, la réponse peut très bien s'appliquer à votre situation .

En substance, il semble que SQL Server puisse avoir un plan d’exécution mis en cache corrompu. Vous avez atteint le mauvais plan avec votre serveur Web, mais SSMS atterrit sur un autre plan car l'indicateur ARITHABORT est défini de manière différente (ce qui n'aurait sinon aucun impact sur votre requête/procédure stockée).

Voir La procédure stockée T-SQL avec ADO.NET appelle une exception SqlTimeoutException pour un autre exemple, avec une explication et une résolution plus complètes.

74
StriplingWarrior

J'ai également constaté que les requêtes sur le Web étaient lentes et rapides dans SSMS et j'ai finalement découvert que le problème s'appelait un paramètre appelé sniffing de paramètre.

La solution pour moi était de changer tous les paramètres utilisés dans le sproc en variables locales.

par exemple. changement:

ALTER PROCEDURE [dbo].[sproc] 
    @param1 int,
AS
SELECT * FROM Table WHERE ID = @param1 

à:

ALTER PROCEDURE [dbo].[sproc] 
    @param1 int,
AS
DECLARE @param1a int
SET @param1a = @param1
SELECT * FROM Table WHERE ID = @param1a

Cela semble étrange, mais cela a résolu mon problème.

45
Zane

Pas pour le spam, mais comme une solution, espérons-le, utile pour les autres, notre système a connu un grand nombre de délais d'attente.

J'ai essayé de configurer la procédure stockée pour qu'elle soit recompilée à l'aide de sp_recompile et cela a résolu le problème du seul SP.

En fin de compte, il y avait un plus grand nombre de SP dont le délai était dépassé, dont beaucoup ne l'avaient jamais fait auparavant, en utilisant DBCC DROPCLEANBUFFERS et DBBC FREEPROCCACHE le taux d'incidents liés aux délais d'attente a considérablement chuté - il existe encore des événements isolés, certains cas où je pense que la régénération du plan prend du temps, et d'autres où les PS sont véritablement sous-performants et nécessitent une réévaluation.

7
Clint

Se pourrait-il que d'autres appels à la base de données effectués avant que l'application Web appelle le SP maintient une transaction ouverte? Cela peut être une raison pour cela SP attendre Lorsque l’application Web l’appelle, c’est-à-dire que vous devez isoler l’appel dans l’application Web (placez-le sur une nouvelle page) pour vous assurer que certaines actions préalables de l’application Web sont à l’origine de ce problème.

4
Tundey

Recompiler simplement la procédure stockée (fonction de table dans mon cas) a fonctionné pour moi

1
Guy Biber

comme @Zane a dit que cela pourrait être dû à la détection de paramètres. J'ai eu le même comportement et j’ai jeté un coup d’œil au plan d’exécution de la procédure et à toutes les instructions du sp consécutives (toutes les instructions copiées de la procédure sont déclarées, les paramètres ont été déclarés sous forme de variables et les valeurs attribuées étaient identiques. les paramètres avaient). Cependant, le plan d'exécution semblait complètement différent. L'exécution de sp prenait 3-4 secondes et les déclarations consécutives contenant exactement les mêmes valeurs étaient instantanément renvoyées.

executionplan

Après quelques recherches sur Google, j'ai trouvé une lecture intéressante à propos de ce comportement: lent dans l'application, rapide dans SSMS?

Lors de la compilation de la procédure, SQL Server ne sait pas que la valeur de @fromdate change, mais la compile en supposant que @fromdate a la valeur NULL. Étant donné que toutes les comparaisons avec NULL renvoient UNKNOWN, la requête ne peut renvoyer aucune ligne, si @fromdate a toujours cette valeur au moment de l'exécution. Si SQL Server prend la valeur d'entrée comme vérité finale, il peut construire un plan avec uniquement une analyse constante qui n'accède pas du tout à la table (exécutez la requête SELECT * FROM Orders WHERE OrderDate> NULL pour en voir un exemple). . Mais SQL Server doit générer un plan qui renvoie le résultat correct, quelle que soit la valeur de @fromdate au moment de l'exécution. D'autre part, il n'y a aucune obligation de construire un plan qui est le meilleur pour toutes les valeurs. Ainsi, dans la mesure où l'hypothèse est qu'aucune ligne ne sera renvoyée, SQL Server prend en charge la recherche d'index.

Le problème était que j'avais des paramètres qui pouvaient rester nuls et s'ils étaient passés à null, ils seraient initialisés avec une valeur par défaut.

create procedure dbo.procedure
    @dateTo datetime = null
begin
    if (@dateTo is null)
    begin
        select @dateTo  = GETUTCDATE()
    end

    select foo
    from dbo.table
    where createdDate < @dateTo
end

Après je l'ai changé pour

create procedure dbo.procedure
    @dateTo datetime = null
begin
    declare @to datetime = coalesce(@dateTo, getutcdate())

    select foo
    from dbo.table
    where createdDate < @to
end 

cela a fonctionné comme un charme encore.

1
TomPez