Plan d'exécution soudainement lent pour le processus stocké


15

J'essaie de comprendre un problème que nous rencontrons avec SQL Server 2000. Nous sommes un site Web modérément transactionnel et nous avons un proc stocké appelé sp_GetCurrentTransactionsqui accepte un identifiant client et deux dates.

Maintenant, selon les dates et le client, cette requête peut renvoyer n'importe quoi de zéro à des milliers de lignes.

Le problème: ce que nous avons vécu, c'est que nous obtiendrons soudainement un certain nombre d'erreurs (généralement Execution Timeout Expiredou similaires) pour un client particulier pendant qu'il essaie d'exécuter ce processus stocké. Nous examinons donc la requête, l'exécutons dans SSMS et constatons qu'elle prend 30 secondes. Nous recompilons donc le proc stocké et -bang- il fonctionne maintenant en 300 ms.

J'en ai parlé à notre DBA. Il m'a dit que la base de données a créé un plan de requête lorsque nous avons créé le proc stocké. Il a dit que c'était un bon plan pour cet ensemble de paramètres, mais si vous y jetez un certain ensemble de paramètres, alors le plan ne sera pas le meilleur plan pour ces données, et donc vous le verrez fonctionner lentement.

Les options qui me sont présentées sont le déplacement de cette requête problématique à partir d'un proc stocké et le retour dans SQL dynamique qui a son plan d'exécution créé à chaque exécution.

Cela me semble être un pas en arrière et j'ai l'impression qu'il doit y avoir un moyen de contourner cela. Existe-t-il un autre moyen de résoudre ce problème?

Toutes les réponses sont appréciées.


y a-t-il une instruction if / else dans le proc? J'ai vu cela se produire lorsque le plan est mis en cache dans l'instruction if, puis essaie de s'exécuter sous le bloc else en utilisant le mauvais plan. Ces erreurs correspondaient-elles à un changement de proc?
Jeremy Gray

@Jeremy: Aucune modification du proc et aucune instruction else / if.
Ciaran Archer du

Réponses:


14

Ce problème est appelé reniflement des paramètres.

Les versions ultérieures de SQL Server vous offrent plus d'options pour y faire face, comme OPTION (RECOMPILE)ou des OPTIMIZE FORconseils.

Vous pouvez essayer de déclarer des variables dans la procédure stockée, d'affecter les valeurs des paramètres aux variables et d'utiliser les variables à la place des paramètres, comme si la plupart du temps vous obtenez un plan raisonnablement satisfaisant.

Normalement, les plans les plus catastrophiques sont ceux qui sont compilés pour des paramètres avec une sélectivité très élevée, mais exécutés avec des paramètres avec une sélectivité faible.

En supposant que le plan généré est plus robuste avec cette approche et satisfaisant pour toutes les valeurs de paramètres, l'avantage de cette approche par rapport à celle suggérée par JNK est qu'elle n'entraîne pas de coût de compilation pour chaque appel.

L'inconvénient est que pour certaines exécutions, le temps d'exécution peut être supérieur à celui d'un plan spécialement conçu pour ces valeurs de paramètre, il s'agit donc d'un compromis entre le temps de compilation et le temps d'exécution.


3
Ou "bind peeking" dans la terminologie Oracle
Gaius

Merci @Gaius, bon à savoir la terminologie de plus d'un SGBDR;)
Andrei Rînea

6

Au lieu d'utiliser du SQL dynamique, vous pouvez toujours simplement changer vos appels de proc en:

EXEC Database.dbo.usp_Myprocedure 'Parameter' WITH RECOMPILE

le WITH RECOMPILE forces (vous l'avez deviné!) Recompilent le plan d'exécution à chaque exécution.

Vous pouvez également inclure WITH RECOMPILEdans la définition du proc stocké:

CREATE PROCEDURE usp.MyProcedure (Parameters)
WITH RECOMPILE
AS
...

2

Vous pouvez également essayer de décider pour la base de données quel plan utiliser, bien que vous vous battiez un peu avec l'optimiseur, il est donc plus fragile que vous ne l'espérez.

La technique est la suivante: divisez la procédure stockée en 2, l'une destinée à un ensemble de paramètres, l'autre à l'autre. Ajoutez des clauses where à chacune de sorte qu’entre elles, elles couvrent tous les cas possibles. Regardez les plans de requête - l'un doit être optimisé pour un ensemble de paramètres, l'autre pour l'autre ensemble. Vous devrez peut-être bricoler la requête pour que cela se produise, ou cela peut ne pas être possible pour votre requête, auquel cas cette approche ne fonctionnera pas.

Faites maintenant vérifier la valeur stockée de votre procédure stockée d'origine et envoyez-la à l'une des deux procédures stockées appropriées du paragraphe précédent.

Cela peut fonctionner, mais c'est une sorte de piratage pour forcer l'optimiseur à fonctionner plus efficacement pour votre requête. Comme tous ces hacks, dans les futures versions de la base de données, cela pourrait être inutile ou même aggraver les choses. Donc, même si cela fonctionne, vous devez décider si cela en vaut la peine.



0

Hmmm ... si nous nous concentrons uniquement sur cette procédure stockée, je serais surpris que l'utilisation du plan d'exécution mis en cache soit à l'origine du problème que vous voyez. Je demanderais à voir le plan d'exécution de la procédure stockée en utilisant un ensemble de paramètres pour le client et les deux dates. Je me demande si un index plus spécifique serait utile -> comme sur customerId, et les deux dates seulement?


2
Pourquoi la surprise? le reniflage de paramètres est un problème assez courant avec ces symptômes et il semble que le DBA ait identifié cela comme le problème.
Martin Smith

@MartinSmith - Je suis un peu surpris que le DBA qui connaît le reniflage de paramètres ne connaisse pas les indices de recompilation ...
JNK

@JNK - C'est vrai. Je ne sais pas pourquoi ils ne le mentionneraient pas.
Martin Smith

0

La dégradation soudaine des performances ressemble à un plan de requête inefficace qui est produit, probablement en raison de statistiques manquantes. Exécutez un profileur SQL Server avec les catégories d'événements "Erreurs et avertissements" définies et voyez s'il existe des avertissements concernant les statistiques manquantes.

Un index peut également vous manquer, ou vous devrez peut-être défragmenter les index car ils peuvent être trop fragmentés pour être utilisés par SQL Server, ce qui fait penser qu'un scan de table produira moins d'E / S.

@JNK soulève un grand point sur les proc stockés - ceux-ci sont compilés à l'avance et le plan de requête sera stocké avec la procédure stockée.

Je ne suis pas nécessairement d'accord avec l'utilisation de WITH RECOMPILE car vous perdez alors l'avantage du plan de requête stocké et réutilisé. Il y a des cas où cela est nécessaire - c'est-à-dire si vos statistiques de distribution dans les tables sous-jacentes diffèrent considérablement entre les appels, mais généralement, une fois que les données dans les tables sont arrivées à maturité, la distribution des données dans les tables variera de manière minimale.

Donc, pour résumer:

  1. Vérifier les statistiques manquantes
  2. Vérifier la fragmentation de l'index
  3. Créer et utiliser un proc stocké
  4. Veuillez renommer le proc - sp_ est un espace de noms de préfixe doucement réservé pour les procs SQL Server du système interne - ce qui fait que SQL Server recherche toujours dans la base de données master les procédures stockées en premier. Renommer le proc usp_ au lieu de sp_ entraînera une augmentation des performances, mais je doute que ce soit votre problème dans ce cas.
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.