J'ai essayé de diagnostiquer des ralentissements dans une application. Pour cela, j'ai enregistré les événements étendus SQL Server .
- Pour cette question, je regarde une procédure stockée particulière.
- Mais il existe un ensemble de base d'une douzaine de procédures stockées qui peuvent également être utilisées comme enquête de pomme à pomme.
- et chaque fois que j'exécute manuellement l'une des procédures stockées, elle s'exécute toujours rapidement
- et si un utilisateur réessaye: il s'exécutera rapidement.
Les temps d'exécution de la procédure stockée varient énormément. Beaucoup d'exécutions de cette procédure stockée retournent en <1s:
Et pour ce seau "rapide" , c'est beaucoup moins que 1s. Il s'agit en fait d'environ 90 ms:
Mais il y a une longue queue d'utilisateurs qui doivent attendre 2s, 3s, 4s secondes. Certains doivent attendre 12, 13, 14 ans. Ensuite, il y a les âmes vraiment pauvres qui doivent attendre 22, 23, 24.
Et après 30 ans, l'application cliente abandonne, abandonne la requête et l'utilisateur doit attendre 30 secondes .
Corrélation pour trouver la causalité
J'ai donc essayé de corréler:
- durée vs lectures logiques
- durée vs lectures physiques
- durée vs temps cpu
Et aucun ne semble donner de corrélation; aucun ne semble être la cause
durée vs lectures logiques : que ce soit un peu ou beaucoup de lectures logiques, la durée fluctue encore énormément :
durée vs lectures physiques : même si la requête n'a pas été servie à partir du cache et que de nombreuses lectures physiques ont été nécessaires, cela n'affecte pas la durée:
duration vs cpu time : que la requête prenne 0s de temps CPU ou 2,5s de temps CPU, les durées ont la même variabilité:
Bonus : j'ai remarqué que la durée v lectures physiques et la durée v temps CPU semblent très similaires. Cela est prouvé si j'essaie de corréler le temps CPU avec les lectures physiques:
Il s'avère que beaucoup d'utilisation du processeur provient des E / S. Qui savait!
Donc, s'il n'y a rien dans le fait d' exécuter la requête qui peut expliquer les différences de temps d'exécution, cela signifie-t-il que c'est quelque chose de non lié au CPU ou au disque dur?
Si le CPU ou le disque dur étaient le goulot d'étranglement; ne serait-ce pas le goulot d'étranglement?
Si nous émettons l'hypothèse que c'est le CPU qui était le goulot d'étranglement; que le CPU est sous-alimenté pour ce serveur:
- alors les exécutions utilisant plus de temps CPU ne prendraient-elles pas plus de temps?
- car ils doivent compléter avec d'autres en utilisant le processeur surchargé?
De même pour les disques durs. Si nous émettons l'hypothèse que le disque dur était un goulot d'étranglement; que les disques durs n'ont pas assez de débit aléatoire pour ce serveur:
- alors les exécutions utilisant plus de lectures physiques ne prendraient-elles pas plus de temps?
- car ils doivent compléter avec d'autres en utilisant les E / S de disque dur surchargées?
La procédure stockée elle-même n'effectue ni n'exige aucune écriture.
- Habituellement, il renvoie 0 lignes (90%).
- Parfois, il renvoie 1 ligne (7%).
- Il renverra rarement 2 lignes (1,4%).
- Et dans le pire des cas, il a renvoyé plus de 2 lignes (une fois renvoyant 12 lignes)
Ce n'est donc pas comme s'il retournait un volume fou de données.
Utilisation du processeur du serveur
L' utilisation du processeur du serveur est en moyenne d'environ 1,8%, avec un pic occasionnel allant jusqu'à 18% - il ne semble donc pas que la charge du processeur soit un problème:
Ainsi, le processeur du serveur ne semble pas surchargé.
Mais le serveur est virtuel ...
Quelque chose en dehors de l'univers?
La seule chose qu'il me reste à imaginer est quelque chose qui existe en dehors de l'univers du serveur.
- si ce n'est pas des lectures logiques
- et ce ne sont pas des lectures physiques
- et ce n'est pas l'utilisation du processeur
- et ce n'est pas une charge CPU
Et ce n'est pas comme si c'était les paramètres de la procédure stockée (car émettre la même requête manuellement et cela ne prend pas 27 secondes - cela prend ~ 0 seconde).
Quoi d'autre pourrait expliquer que le serveur prenne parfois 30 secondes, au lieu de 0 seconde, pour exécuter la même procédure stockée compilée.
- points de contrôle?
C'est un serveur virtuel
- l'hôte surchargé?
- une autre VM sur le même hôte?
Passer en revue les événements étendus du serveur; il ne se passe rien d'autre en particulier lorsqu'une requête prend soudainement 20 secondes. Il fonctionne bien, puis décide de ne pas fonctionner correctement:
- 2 secondes
- 1 seconde
- 30 secondes
- 3 secondes
- 2 secondes
Et je ne trouve aucun autre élément particulièrement pénible. Ce n'est pas pendant la sauvegarde du journal des transactions toutes les 2 heures.
Que pourrait- il être d'autre?
Y a-t-il autre chose que je peux dire en plus: "le serveur" ?
Modifier : corréler selon l'heure de la journée
J'ai réalisé que j'avais corrélé les durées avec tout:
- lectures logiques
- lectures physiques
- l'utilisation du processeur
Mais la seule chose à laquelle je n'ai pas corrélé c'était l' heure de la journée . La sauvegarde du journal des transactions toutes les 2 heures est peut-être un problème.
Ou peut - être les ralentissements ne se produisent dans les postes de contrôle au cours mandrins?
Nan:
Intel Xeon Gold Quad-core 6142.
Modifier - Les gens font l'hypothèse du plan d'exécution des requêtes
Les gens émettent l'hypothèse que les plans d'exécution des requêtes doivent être différents entre "rapide" et "lent". Ils ne sont pas.
Et nous pouvons le voir immédiatement après inspection.
Nous savons que la plus longue durée des questions n'est pas due à un "mauvais" plan d'exécution:
- celui qui a pris des lectures plus logiques
- celui qui a consommé plus de CPU à partir de plus de jointures et de recherches de clés
Parce que si une augmentation des lectures, ou une augmentation du processeur, était une cause de l'augmentation de la durée des requêtes, nous l'aurions déjà vu ci-dessus. Il n'y a pas de corrélation.
Mais essayons de corréler la durée avec la métrique du produit de la zone de lecture du processeur:
Il y a encore moins de corrélation - ce qui est un paradoxe.
Modifier : mise à jour des diagrammes de dispersion pour contourner un bogue dans les diagrammes de dispersion Excel avec un grand nombre de valeurs.
Prochaines étapes
Mes prochaines étapes seront de demander à quelqu'un d'avoir à générer des événements sur le serveur pour les requêtes bloquées - après 5 secondes:
EXEC sp_configure 'blocked process threshold', '5';
RECONFIGURE
Cela n'explique pas si les requêtes sont bloquées pendant 4 secondes. Mais peut-être que tout ce qui bloque une requête pendant 5 secondes en bloque également certains pendant 4 secondes.
Les plans lents
Voici le plan lent des deux procédures stockées en cours d'exécution:
- `EXECUTE FindFrob @CustomerID = 7383, @StartDate = '20190725 04: 00: 00.000', @EndDate = '20190726 04: 00: 00.000'
- `EXECUTE FindFrob @CustomerID = 7383, @StartDate = '20190725 04: 00: 00.000', @EndDate = '20190726 04: 00: 00.000'
La même procédure stockée, avec les mêmes paramètres, s'exécute dos à dos:
| Duration (us) | CPU time (us) | Logical reads | Physical reads |
|---------------|---------------|---------------|----------------|
| 13,984,446 | 47,000 | 5,110 | 771 |
| 4,603,566 | 47,000 | 5,126 | 740 |
Appel 1:
|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
|--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
| |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
| | |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
| | | |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
| | | | |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
| | | | |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
| | | |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
| | |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
| |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]), WHERE:([Contos
|--Filter(WHERE:([Expr1009]>(1)))
|--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
|--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
|--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC
Appel 2
|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
|--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
| |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
| | |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
| | | |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
| | | | |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
| | | | |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
| | | |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
| | |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
| |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]), WHERE:([Contos
|--Filter(WHERE:([Expr1009]>(1)))
|--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
|--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
|--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC
Il est logique que les plans soient identiques; il exécute la même procédure stockée, avec les mêmes paramètres.
la source
Réponses:
Jetez un œil aux wait_stats et cela montrera quels sont les plus gros goulots d'étranglement sur votre serveur SQL.
J'ai récemment rencontré un problème où une application externe était lente par intermittence. Cependant, l'exécution des procédures stockées sur le serveur lui-même était toujours rapide.
La surveillance des performances n'a montré aucun souci du tout avec les caches SQL ou l'utilisation de la RAM et des E / S sur le serveur.
Ce qui a aidé à affiner l'enquête était d'interroger les statistiques d'attente collectées par SQL dans
sys.dm_os_wait_stats
L'excellent script sur le site Web SQLSkills vous montrera ceux que vous rencontrez le plus. Vous pouvez ensuite affiner votre recherche pour identifier les causes.
Une fois que vous savez quelles sont les attentes, ce script vous aidera à préciser quelle session / base de données connaît les attentes:
La requête ci-dessus et d'autres détails proviennent du site Web MSSQLTips .
Le
sp_BlitzFirst
script du site Web de Brent Ozar vous montrera également ce qui cause les ralentissements.la source