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:
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?
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.
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.
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.
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.
Recompiler simplement la procédure stockée (fonction de table dans mon cas) a fonctionné pour moi
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.
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.