Lorsqu'une requête SQL précédemment rapide commence à s'exécuter lentement, où dois-je chercher la source du problème?

37

Contexte

J'ai une requête en cours d'exécution sur SQL Server 2008 R2 qui joint et / ou joint à gauche environ 12 "tables" différentes. La base de données est assez volumineuse avec de nombreuses tables de plus de 50 millions de lignes et environ 300 tables différentes. C'est pour une grande entreprise qui a 10 entrepôts à travers le pays. Tous les entrepôts lisent et écrivent dans la base de données. Donc, c'est assez grand et pas mal occupé.

La requête avec laquelle j'ai des problèmes ressemble à ceci:

select t1.something, t2.something, etc.
from Table1 t1
    inner join Table2 t2 on t1.id = t2.t1id
    left outer join (select * from table 3) t3 on t3.t1id = t1.t1id
    [etc]...
where t1.something = 123

Notez que l'une des jointures se trouve sur une sous-requête non corrélée.

Le problème est que, à compter de ce matin, sans aucune modification (que je ne connais pas d’aucune personne de mon équipe), la requête, qui prend généralement environ 2 minutes, a pris une heure et demie - quand elle couru du tout. Le reste de la base de données est très bien. J'ai retiré cette requête du sproc dans lequel elle s'exécute habituellement et je l'ai exécutée dans SSMS avec des variables de paramètre codées en dur avec la même lenteur.

L'étrangeté est que lorsque je prends la sous-requête non corrélée et la jette dans une table temporaire, puis que je l'utilise au lieu de la sous-requête, la requête s'exécute correctement. Aussi (et c'est le plus étrange pour moi) si j'ajoute ce morceau de code à la fin de la requête, la requête est géniale:

and t.name like '%'

J'ai conclu (peut-être à tort) de ces petites expériences que la raison du ralentissement est due à la manière dont le plan d'exécution mis en cache de SQL est configuré: lorsque la requête est un peu différente, elle doit créer un nouveau plan d'exécution.

Ma question est la suivante: lorsqu'une requête qui fonctionnait rapidement auparavant commence soudainement à fonctionner lentement au milieu de la nuit et que rien d'autre n'est affecté à l'exception de cette requête, comment puis-je résoudre le problème et comment l'empêcher de se reproduire à l'avenir ? Comment savoir ce que SQL fait en interne pour le ralentir (si la mauvaise requête s'exécute, je peux obtenir son plan d'exécution mais il ne s'exécutera pas - le plan d'exécution attendu pourrait peut-être me donner quelque chose?)? Si ce problème concerne le plan d'exécution, comment empêcher SQL de penser que des plans d'exécution vraiment merdiques sont une bonne idée?

De plus, ce n'est pas un problème avec le reniflage de paramètres. Je l'ai déjà vu auparavant, et ce n'est pas le cas, car même lorsque je code les variables dans SSMS, les performances sont lentes.

Trevor
la source
Pourriez - vous le plan de requête (la lente) ici: brentozar.com/pastetheplan
MJH

Réponses:

32

Lorsqu'une requête qui fonctionnait rapidement commence soudainement à s'exécuter lentement au milieu de la nuit et que rien d'autre n'est affecté à l'exception de cette requête, comment puis-je résoudre le problème ...?

Vous pouvez commencer par vérifier si le plan d'exécution est toujours dans le cache. Vérifiez sys.dm_exec_query_stats, sys.dm_exec_procedure_statset sys.dm_exec_cached_plans. Si le plan d'exécution incorrect est toujours mis en cache, vous pouvez l'analyser et vous pouvez également vérifier les statistiques d'exécution. Les statistiques d'exécution contiendront des informations telles que les lectures logiques, le temps CPU et le temps d'exécution. Celles-ci peuvent donner de fortes indications sur le problème (par exemple, numérisation volumineuse ou blocage). Voir Identification des requêtes problématiques pour une explication sur l'interprétation des données.

De plus, ce n'est pas un problème avec le reniflage de paramètres. Je l'ai déjà vu auparavant, et ce n'est pas le cas, car même lorsque je code les variables dans SSMS, les performances sont lentes.

Je ne suis pas convaincu. Le codage en dur des variables dans SSMS ne prouve pas que le plan d’exécution erronée précédent n’a pas été compilé avec une entrée asymétrique. Veuillez lire les paramètres de recherche, d’incorporation et d’option RECOMPILE pour un très bon article sur le sujet. Lent dans l'application, rapide dans SSMS? Comprendre les performances mystères est une autre excellente référence.

J'ai conclu (peut-être à tort) de ces petites expériences que la raison du ralentissement est due à la manière dont le plan d'exécution mis en cache de SQL est configuré: lorsque la requête est un peu différente, elle doit créer un nouveau plan d'exécution.

Cela peut être facilement testé. SET STATISTICS TIME ONvous montrera la compilation en fonction du temps d'exécution. SQL Server: les compteurs de performance des statistiques indiqueront également si la compilation pose problème (franchement, je trouve cela peu probable).

Cependant, vous pouvez toucher à quelque chose de similaire: la porte d'octroi de requête. Lisez Comprendre l’allocation de mémoire du serveur SQL pour plus de détails. Si votre requête demande une subvention importante à un moment donné, aucune mémoire n'est disponible, il faudra patienter et l'application se présentera comme une "exécution lente". L'analyse des statistiques des informations d'attente vous indiquera si c'est le cas.

Pour une discussion plus générale sur ce qu'il faut mesurer et ce qu'il faut rechercher, voir Comment analyser les performances de SQL Server

Remus Rusanu
la source
7

Il s'agit d'un fléau de l'exécution de requêtes complexes dans SQL Server. Heureusement, cela n'arrive pas souvent.

Examinez le plan de requête pour la requête (quand elle s'exécute lentement). Je suppose que vous allez trouver une jointure de boucle imbriquée se produisant une ou plusieurs fois sur des tables sans index pour la jointure. Cela ralentit vraiment les choses. Pour avancer rapidement, la solution consiste à utiliser un indice. Ajoutez ce qui suit à la fin de la requête:

OPTION (MERGE JOIN, HASH JOIN)

Cela a généralement résolu ce problème pour moi dans le passé.

Il se peut que des modifications subtiles apportées à la table (ou à la disponibilité d'espace temporaire) obligent l'optimisation SQL à préférer un algorithme de jointure plus lent. Cela peut être assez subtil et assez soudain. Lorsque vous créez une table temporaire, l'optimiseur dispose de plus d'informations sur la table (telles que sa taille) pour pouvoir générer un meilleur plan.

Gordon Linoff
la source
1
Le plan d'exécution utilise en effet une jointure de boucle imbriquée. Cependant, lorsque je place le conseil que vous suggérez, le message d'erreur suivant s'affiche: "Le processeur de requêtes n'a pas pu établir de plan de requête en raison des conseils définis dans cette requête. Soumettez à nouveau la requête sans spécifier de conseils et sans utiliser SET FORCEPLAN." Lorsque j'ajoute OPTION (LOOP JOIN) bien sûr, un plan d'exécution sera créé, mais le problème persiste. Avez-vous rencontré ce problème? On dirait qu'il pense qu'il faut BESOIN pour joindre en boucle.
@Trevor. . . Non, je n'ai pas vraiment vu ce problème. Peut-être que votre requête effectue des non-équijointures (sans utiliser des signes égaux) et que l'optimiseur doit utiliser des jointures de boucle imbriquées.
Gordon Linoff
3

Habituellement, c'est un index manquant qui cause ce genre de problème.

Ce que je fais habituellement, c'est exécuter la requête à l'aide de SQL Management Studio et activer "Inclure le plan d'exécution réel (CTRL + M)" et rechercher la jointure ayant le pourcentage le plus élevé.

L'application ne se concentre pas sur le goulot d'étranglement, mais vous pouvez le trouver "rapidement" en regardant simplement dans le résultat.

exemple ici: 48PercentForTop

Xavier
la source
2
un index ne va pas soudainement "disparaître", donc si cela peut améliorer les performances, il n'explique pas le problème
Fowl
3

J'ai récemment rencontré ce même problème qui m'a amené à cette page.

@MartinSmith était sur la bonne voie lorsqu'il a recommandé de mettre à jour vos statistiques et d'expliquer votre plan. J'aimerais ajouter que vous devriez également essayer de vous assurer que vous jetez un coup d'œil aux travaux / requêtes en cours d'exécution qui pourraient créer des verrous et ralentir ainsi le temps de réponse.

Dans mon cas, le coupable était la statistique de la table de collecte des tâches. Pour une raison quelconque, il n’a pas terminé dans la fenêtre qu’il aurait dû et a continué à fonctionner lorsque les utilisateurs ont repris. J'ai trouvé le processus, je l'ai tué et les requêtes ont recommencé à répondre.

J'espère que ça aidera quelqu'un d'autre

Daffyjeje
la source
0

Vous devez également vérifier si une sauvegarde de serveur ou des travaux d'archivage \ indexation sont en cours d'exécution lorsque vous constatez un problème de performances dans T-SQL \ Procedure.

Amol Nimbalkar
la source