J'ai un cas typique où le reniflage de paramètres fait atterrir un «mauvais» plan d'exécution dans le cache du plan, ce qui rend les exécutions ultérieures de ma procédure stockée très lentes. Je peux "résoudre" ce problème avec des variables locales,, OPTIMIZE FOR ... UNKNOWN
et OPTION(RECOMPILE)
. Cependant, je peux également plonger dans la requête et essayer de l'optimiser.
J'essaie de déterminer si je devrais : étant donné le temps limité pour résoudre les problèmes, je voudrais savoir le coût de ne pas le faire. À mon avis, si je continue OPTION(RECOMPILE)
, l'effet net est qu'un plan de requête est recréé chaque fois que la requête est exécutée. Donc, je pense que je dois savoir:
Comment connaître le coût de création d' un plan de requête?
Pour répondre à ma propre question, j'ai googlé (par exemple avec cette requête ), et j'ai parcouru la documentation des colonnes pour le dm_exec_query_stats
DMV . J'ai également inspecté la fenêtre de sortie dans SSMS pour "Plan de requête réel" pour trouver ces informations. Enfin, je l' ai cherché DBA.SE . Aucun de ceux-ci n'a conduit à une réponse.
Quelqu'un peut-il me le dire? Est-il possible de trouver ou de mesurer le temps nécessaire à la création d'un plan?
la source
Réponses:
Vous pouvez consulter les propriétés du nœud racine dans le plan de requête, par exemple:
(capture d'écran de l' explorateur gratuit Sentry One Plan )
Ces informations sont également disponibles en interrogeant le cache du plan, par exemple à l'aide d'une requête basée sur les relations suivantes:
Pour un traitement complet des options dont vous disposez pour gérer ce type de requêtes, consultez l'article récemment mis à jour d' Erland Sommarskog .
la source
En supposant que le "coût" est en termes de temps (bien que vous ne sachiez pas quoi d'autre il pourrait être en termes de ;-), alors vous devriez au moins avoir une idée de cela en faisant quelque chose comme ceci:
Le premier élément signalé dans l'onglet "Messages" doit être:
Je l'exécuterais au moins 10 fois et ferais la moyenne des millisecondes "CPU" et "Elapsed".
Dans l'idéal, vous l'exécuteriez dans Production afin d'obtenir une estimation de l'heure réelle, mais les utilisateurs sont rarement autorisés à vider le cache du plan dans Production. Heureusement, à partir de SQL Server 2008, il est devenu possible d'effacer un plan spécifique du cache. Dans ce cas, vous pouvez effectuer les opérations suivantes:
Cependant, en fonction de la variabilité des valeurs transmises pour le ou les paramètres à l'origine du "mauvais" plan mis en cache, il existe une autre méthode à considérer qui est un juste milieu entre
OPTION(RECOMPILE)
etOPTION(OPTIMIZE FOR UNKNOWN)
: Dynamic SQL. Oui, je l'ai dit. Et je veux dire même Dynamic SQL non paramétré. Voici pourquoi.Vous avez clairement des données qui ont une distribution inégale, au moins en termes d'une ou plusieurs valeurs de paramètres d'entrée. Les inconvénients des options mentionnées sont:
OPTION(RECOMPILE)
générera un plan pour chaque exécution et vous ne pourrez jamais bénéficier d'une quelconque réutilisation du plan, même si les valeurs des paramètres retransmises sont identiques aux exécutions précédentes. Pour les procs qui sont appelés fréquemment - une fois toutes les quelques secondes ou plus fréquemment - cela vous évitera une situation horrible occasionnelle, mais vous laissera toujours dans une situation toujours pas si géniale.OPTION(OPTIMIZE FOR (@Param = value))
générera un plan basé sur cette valeur particulière, ce qui pourrait aider plusieurs cas, mais vous laissera toujours ouvert au problème actuel.OPTION(OPTIMIZE FOR UNKNOWN)
générera un plan basé sur ce qui équivaut à une distribution moyenne, ce qui aidera certaines requêtes mais en blessera d'autres. Cela devrait être le même que l'option d'utiliser des variables locales.Le SQL dynamique, cependant, une fois correctement effectué , permettra aux différentes valeurs transmises d'avoir leurs propres plans de requête séparés qui sont idéaux (enfin, autant qu'ils vont l'être). Le coût principal ici est que, à mesure que la variété des valeurs transmises augmente, le nombre de plans d'exécution dans le cache augmente et ils prennent de la mémoire. Les coûts mineurs sont:
besoin de valider les paramètres de chaîne pour empêcher les injections SQL
éventuellement besoin de configurer un certificat et un utilisateur basé sur un certificat pour maintenir une abstraction de sécurité idéale, car Dynamic SQL nécessite des autorisations de table directes.
Donc, voici comment j'ai géré cette situation lorsque j'ai eu des procs qui ont été appelés plus d'une fois par seconde et qui ont touché plusieurs tables, chacune avec des millions de lignes. J'avais essayé
OPTION(RECOMPILE)
mais cela s'est avéré beaucoup trop préjudiciable au processus dans les 99% des cas qui n'avaient pas le problème de reniflage de paramètre / problème de plan mis en cache. Et n'oubliez pas que l'un de ces procs contenait environ 15 requêtes et seulement 3 à 5 d'entre elles ont été converties en Dynamic SQL comme décrit ici; Le SQL dynamique n'a été utilisé que s'il était nécessaire pour une requête particulière.S'il existe plusieurs paramètres d'entrée dans la procédure stockée, déterminez ceux qui sont utilisés avec des colonnes qui ont des distributions de données très disparates (et par conséquent provoquent ce problème) et ceux qui sont utilisés avec des colonnes qui ont des distributions plus régulières (et ne devraient pas être à l'origine de ce problème).
Générez la chaîne Dynamic SQL à l'aide de paramètres pour les paramètres d'entrée proc associés à des colonnes réparties uniformément. Ce paramétrage permet de réduire l'augmentation résultante des plans d'exécution dans le cache liée à cette requête.
Pour les autres paramètres associés à des distributions très variées, ceux-ci doivent être concaténés dans Dynamic SQL sous forme de valeurs littérales. Puisqu'une requête unique est déterminée par toute modification du texte de la requête, avoir
WHERE StatusID = 1
est une requête différente, et donc, un plan de requête différent, qu'avoirWHERE StatusID = 2
.Si l'un des paramètres d'entrée de proc qui doivent être concaténés dans le texte de la requête sont des chaînes, ils doivent être validés pour se protéger contre l'injection SQL (bien que cela soit moins susceptible de se produire si les chaînes transmises sont générées par le application et pas un utilisateur, mais quand même). Faites au moins le
REPLACE(@Param, '''', '''''')
pour vous assurer que les guillemets simples deviennent des guillemets simples échappés.Si nécessaire, créez un certificat qui sera utilisé pour créer un utilisateur et signez la procédure stockée de sorte que les autorisations de table directes ne seront accordées qu'au nouvel utilisateur basé sur un certificat et non
[public]
aux utilisateurs qui ne devraient pas autrement avoir de telles autorisations. .Exemple de proc:
la source
OPTION
sur ma requête), et ne me ferait pas trop de mal car ce sproc est bien exploité dans les tests d'intégration. - Dans tous les cas: merci pour vos idées!