web-dev-qa-db-fra.com

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

Contexte

J'ai une requête en cours d'exécution contre 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 possède 10 entrepôts à travers le pays. Tous les entrepôts lisent et écrivent dans la base de données. C'est donc assez grand et assez occupé.

La requête avec laquelle je rencontre 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 qu'à partir de ce matin, sans aucune modification (que moi ou un membre de mon équipe connaissons) du système, la requête qui prend généralement environ 2 minutes à exécuter, a commencé à prendre une heure et demie - lorsqu'elle couru du tout. Le reste de la base de données bourdonne 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ètres 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 à la place 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 fonctionne très bien:

and t.name like '%'

J'ai conclu (peut-être à tort) à partir de ces petites expériences que la raison du ralentissement est due à la façon 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 commence soudainement à s'exécuter lentement au milieu de la nuit et que rien d'autre n'est affecté à l'exception de cette seule requête, comment puis-je la résoudre et comment puis-je la résoudre? l'empêcher de se produire à l'avenir? Comment savoir ce que SQL fait en interne pour le ralentir (si la mauvaise requête s'exécutait, je pourrais obtenir son plan d'exécution mais il ne fonctionnera pas - peut-être que le plan d'exécution attendu me donnerait quelque chose?)? Si ce problème concerne le plan d'exécution, comment empêcher SQL de penser que les 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 en dur les variables dans SSMS, j'obtiens toujours des performances lentes.

39
Trevor

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 seule requête, comment puis-je la résoudre ...?

Vous pouvez commencer par vérifier si le plan d'exécution est toujours dans le cache. Vérifier sys.dm_exec_query_stats , sys.dm_exec_procedure_stats et sys.dm_exec_cached_plans . Si le mauvais plan d'exécution 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 comme des lectures logiques, le temps CPU et le temps d'exécution. Celles-ci peuvent donner des indications solides sur le problème (par exemple, balayage volumineux ou blocage). Voir Identification des requêtes problématiques pour une explication sur la façon d'interpréter les 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 en dur les variables dans SSMS, j'obtiens toujours des performances lentes.

Je ne suis pas convaincu. Les variables de codage en dur dans SSMS ne prouvent pas que le mauvais plan d'exécution passé n'a pas été compilé avec une entrée asymétrique. Veuillez lire Parameter Sniffing, Embedding, and the RECOMPILE Options pour un très bon article sur le sujet. Lent dans l'application, rapide dans SSMS? Comprendre les mystères de performance est une autre excellente référence.

J'ai conclu (peut-être à tort) à partir de ces petites expériences que la raison du ralentissement est due à la façon 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 ON vous montrera le temps de compilation vs d'exécution. SQL Server: Statistiques les compteurs de performances révéleront également si la compilation est un problème (franchement, je le trouve peu probable).

Cependant, il y a quelque chose de similaire que vous pouvez frapper: la porte d'accord de requête. Lisez Comprendre l'allocation de mémoire du serveur SQL pour plus de détails. Si votre requête demande une allocation importante à un moment où aucune mémoire n'est disponible, elle devra attendre et tout cela ressemblera à une "exécution lente" pour l'application. Analyser wait info stats révélera si c'est le cas.

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

33
Remus Rusanu

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

Regardez le plan de requête pour la requête (lorsqu'elle s'exécute lentement). Je suppose que vous trouverez 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, le moyen de résoudre ce problème est de donner 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é.

Ce qui peut arriver, c'est que des modifications subtiles de la table (ou de la disponibilité de l'espace temporaire) font que l'optimisation SQL préfère 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), afin de pouvoir générer un meilleur plan.

7
Gordon Linoff

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

@MartinSmith était sur quelque chose quand il a recommandé de mettre à jour vos statistiques et d'expliquer votre plan. Je voudrais ajouter que vous devriez également essayer de vous assurer de jeter un œil aux travaux/requêtes en cours qui peuvent créer des verrous et ainsi ralentir le temps de réponse.

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

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

3
daffyjeje

Il s'agit généralement d'un index manquant à l'origine de ce type 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 découvrir quelle jointure a 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 le résultat.

exemple ici: 48PercentForTop

3
Xavier

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

0
Amol Nimbalkar