Sur l'un de nos clients, nous avons eu des problèmes de performances sur notre application. Il s'agit d'une application Web .NET 3.5 qui consomme et met à jour des données sur une base de données SQL Server. Actuellement, notre environnement de production se compose d'une machine Windows 2008 R2 comme frontal et d'un cluster SQL Server 2008 R2 à l'arrière. Notre application utilise COM + et MSDTC pour se connecter à la base de données.
Voici ce qui se passe: nos utilisateurs finaux se plaignent parfois de lenteur dans l'application. Certaines pages prennent plus de temps à charger que prévu. Tout en essayant de comprendre ce qui se passe, j'ai réussi à découvrir un comportement étrange du côté de la base de données qui peut être la cause de la dégradation des performances. J'ai remarqué que parfois il y a des instructions SQL qui prennent beaucoup plus de temps à exécuter que ce qui serait attendu. J'ai réussi à identifier certaines de ces instructions (principalement des invocations de certaines des procédures stockées de notre application) à l'aide d'une trace de profileur (avec le modèle TSQL_Duration) pour identifier les requêtes de longue durée.
Le problème est que lorsque j'exécute ces procédures stockées directement sur la base de données sur SQL Management Studio, elles prennent parfois longtemps (environ 7/8 secondes), d'autres fois elles sont rapides (moins de 1 seconde). Je ne sais pas pourquoi cela se produit et cela me rend fou, car la machine SQL (4 cœurs, 32 Go) n'est utilisée par aucune autre application, et ces requêtes ne devraient pas prendre autant de temps à s'exécuter.
N'étant pas un DBA ou un gourou de SQL Server, j'ai essayé de regarder quelques trucs qui pourraient m'aider à comprendre le problème. Voici les étapes que j'ai prises pour essayer de résoudre le problème et ce que j'ai découvert jusqu'à présent:
- Tout le code TSQL appelé par l'application est écrit dans des procédures stockées.
- J'ai identifié certaines des requêtes de longue durée sur le profileur SQL Server, mais lorsque je les exécute sur Management Studio, elles prennent beaucoup de temps (de 4 à 10 secondes) ou s'exécutent rapidement (moins de 1 seconde). J'exécute exactement les mêmes requêtes avec les mêmes données transmises dans les paramètres. Ces requêtes sont principalement des procédures stockées contenant des instructions select.
- J'ai essayé de regarder les statistiques des attentes et des files d'attente pour essayer de déterminer s'il y a des processus en attente sur certaines ressources. J'ai exécuté la requête suivante:
WITH Waits AS
(SELECT
wait_type,
wait_time_ms / 1000.0 AS WaitS,
(wait_time_ms - signal_wait_time_ms) / 1000.0 AS ResourceS,
signal_wait_time_ms / 1000.0 AS SignalS,
waiting_tasks_count AS WaitCount,
100.0 * wait_time_ms / SUM (wait_time_ms) OVER() AS Percentage,
ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS RowNum
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN (
'CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE', 'SLEEP_TASK',
'SLEEP_SYSTEMTASK', 'SQLTRACE_BUFFER_FLUSH', 'WAITFOR', 'LOGMGR_QUEUE',
'CHECKPOINT_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT', 'BROKER_TO_FLUSH',
'BROKER_TASK_STOP', 'CLR_MANUAL_EVENT', 'CLR_AUTO_EVENT', 'DISPATCHER_QUEUE_SEMAPHORE',
'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'BROKER_EVENTHANDLER',
'TRACEWRITE', 'FT_IFTSHC_MUTEX', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
'BROKER_RECEIVE_WAITFOR', 'ONDEMAND_TASK_QUEUE', 'DBMIRROR_EVENTS_QUEUE',
'DBMIRRORING_CMD', 'BROKER_TRANSMITTER', 'SQLTRACE_WAIT_ENTRIES',
'SLEEP_BPOOL_FLUSH', 'SQLTRACE_LOCK')
)
SELECT
W1.wait_type AS WaitType,
CAST (W1.WaitS AS DECIMAL(14, 2)) AS Wait_S,
CAST (W1.ResourceS AS DECIMAL(14, 2)) AS Resource_S,
CAST (W1.SignalS AS DECIMAL(14, 2)) AS Signal_S,
W1.WaitCount AS WaitCount,
CAST (W1.Percentage AS DECIMAL(4, 2)) AS Percentage,
CAST ((W1.WaitS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgWait_S,
CAST ((W1.ResourceS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgRes_S,
CAST ((W1.SignalS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgSig_S
FROM Waits AS W1
INNER JOIN Waits AS W2 ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type, W1.WaitS, W1.ResourceS, W1.SignalS, W1.WaitCount, W1.Percentage
HAVING SUM (W2.Percentage) - W1.Percentage < 95; -- percentage threshold
GO
Voici ce que j'ai découvert:
- Après avoir réinitialisé les statistiques à l'aide de DBCC SQLPERF (environ 1 ou 2 heures après), les types d'attente que j'ai le plus sont SOS_SCHEDULER_YIELD et WRITELOG
- Au fil du temps (après environ 1 jour d'exécution), les types d'attente les plus fréquents dans la base de données sont CXPACKET (67%) et OLEDB (17%), même si le temps d'attente moyen pour chacun n'est pas long. J'ai également remarqué que les instructions les plus longues identifiées sur SQL Profiler sont des appels à des procédures stockées qui retournent plus d'un jeu de résultats (souvent 3). Peut-il y avoir un problème de paralellisme ici? Existe-t-il un moyen d’essayer d’identifier si c’est la cause du problème?
- J'ai lu quelque part que les attentes OLEDB peuvent être causées par des appels aux ressources OLEDB comme les serveurs liés. Nous avons un serveur lié pour se connecter à une machine des services d'indexation (MSIDXS), mais aucune des déclarations identifiées comme étant en cours d'exécution n'utilise ce serveur lié.
- Le temps d'attente moyen le plus élevé dont je dispose concerne les attentes de type LCK_M_X (environ 1,5 seconde en moyenne), mais ces types d'attente ne se produisent pas très souvent par rapport à d'autres types (par exemple, 64 attentes LCK_M_X contre 10 823 CXPACKET attendent sur la même période) ).
- Une chose que j'ai remarquée est que le service MSDTC n'est pas en cluster. Le service SQL Server est en cluster mais pas MSDTC. Peut-il y avoir un impact sur les performances à cause de cela? Nous utilisons MSDTC parce que notre application utilise Enterprise Services (DCOM) pour accéder à la base de données, mais les serveurs n'ont pas été installés et configurés par nous, mais par notre client.
Quelqu'un peut-il m'aider à mieux comprendre ces données? Quelqu'un peut-il m'aider à comprendre ce qui peut arriver? Puis-je faire quelque chose sur le serveur pour essayer de comprendre les choses? Dois-je parler à l'équipe de développement d'applications?
exec()
fonction expliquerait le comportement observé. Dans ce cas, l'utilisationsp_executesql
résout normalement les problèmes liés aux instructions SQL dynamiques.