J'ai récemment rencontré un niveau élevé de TRANSACTION_MUTEX
temps d'attente cumulé sur un système client. Je ne me souvenais pas d'un cas où j'avais vu ce type d'attente en haut de la liste des "attentes élevées" et j'étais curieux de savoir quels facteurs pouvaient augmenter ce type de temps d'attente global.
La définition de la documentation en ligne de TRANSACTION_MUTEX
est qu'il "se produit lors de la synchronisation de l'accès à une transaction par plusieurs lots". Peu de domaines du moteur SQL Server exposent ce type de fonctionnalité, donc mon enquête s'est limitée aux technologies suivantes :
- Le
sp_getbindtoken
obsolète etsp_bindsession
procédures stockées système utilisées pour gérer les connexions liées - Transactions distribuées
- MARS (ensembles de résultats actifs multiples)
Mon objectif était de tester chaque technologie et de voir si elle influençait le TRANSACTION_MUTEX
type d'attente.
Le premier test que j'ai effectué utilisait le sp_getbindtoken
obsolète et sp_bindsession
procédures stockées. Le sp_getbindtoken
renvoie un identifiant de transaction qui peut ensuite être utilisé par sp_bindsession
pour lier plusieurs sessions ensemble sur la même transaction.
Avant chaque scénario de test, je me suis assuré d'effacer les statistiques d'attente de mon instance SQL Server de test :
DBCC SQLPERF('waitstats', CLEAR); GO
Mon instance SQL Server de test exécutait SQL Server 2012 SP1 Developer Edition (11.0.3000). J'ai utilisé l'exemple de base de données Credit, bien que vous puissiez utiliser n'importe quel autre type d'exemple de base de données comme AdventureWorks si vous le souhaitez, car le schéma et la distribution des données ne sont pas directement pertinents pour le sujet de cet article et n'étaient pas nécessaires pour conduire le TRANSACTION_MUTEX
temps d'attente.
sp_getbindtoken / sp_bindsession
Dans la première fenêtre de session de SQL Server Management Studio, j'ai exécuté le code suivant pour commencer une transaction et générer le jeton de liaison pour l'inscription par les autres sessions planifiées :
USE Credit; GO BEGIN TRANSACTION; DECLARE @out_token varchar(255); EXECUTE sp_getbindtoken @out_token OUTPUT; SELECT @out_token AS out_token; GO
Cela a renvoyé un @out_token
de S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---
. Dans deux fenêtres de requête SQL Server Management Studio distinctes, j'ai exécuté le code suivant pour rejoindre les sessions existantes (accès à la transaction partagée) :
USE Credit; GO EXEC sp_bindsession 'S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---';
Et avec la première fenêtre de session toujours ouverte, j'ai commencé la boucle suivante pour mettre à jour la table de la table de facturation avec une date de facturation égale à la date et à l'heure actuelles, puis j'ai exécuté la même logique dans les deux autres fenêtres (trois sessions actives dans la boucle):
WHILE 1 = 1 BEGIN UPDATE dbo.charge SET charge_dt = SYSDATETIME(); END
Après quelques secondes, j'ai annulé chaque requête en cours d'exécution. Sur les trois sessions, une seule a pu réellement effectuer des mises à jour, même si les deux autres sessions étaient activement associées à la même transaction. Et si je regardais le TRANSACTION_MUTEX
type d'attente, je peux voir qu'il a effectivement incrémenté :
SELECT [wait_type], [waiting_tasks_count], [wait_time_ms], [max_wait_time_ms], [signal_wait_time_ms] FROM sys.dm_os_wait_stats WHERE wait_type = 'TRANSACTION_MUTEX';
Les résultats de ce test particulier étaient les suivants :
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 2 181732 93899 0
Je vois donc qu'il y avait deux tâches en attente (les deux sessions qui essayaient simultanément de mettre à jour la même table via la boucle). Comme je n'avais pas exécuté SET NOCOUNT ON
, j'ai pu voir que seul le premier UPDATE
exécuté loop a reçu des modifications. J'ai essayé cette technique similaire en utilisant quelques variantes différentes (par exemple - quatre sessions globales, avec trois en attente) - et le TRANSACTION_MUTEX
l'incrémentation a montré un comportement similaire. J'ai aussi vu le TRANSACTION_MUTEX
accumulation lors de la mise à jour simultanée d'une table différente pour chaque session ; ainsi, les modifications sur le même objet dans une boucle n'étaient pas nécessaires pour reproduire le TRANSACTION_MUTEX
accumulation de temps d'attente.
Transactions distribuées
Mon prochain test consistait à voir si TRANSACTION_MUTEX
le temps d'attente a été incrémenté pour les transactions distribuées. Pour ce test, j'ai utilisé deux instances SQL Server et un serveur lié connecté entre les deux. MS DTC était en cours d'exécution et correctement configuré, et j'ai exécuté le code suivant qui a effectué un DELETE
local et un DELETE
distant via le serveur lié, puis annulé les modifications :
USE Credit; GO SET XACT_ABORT ON; -- Assumes MS DTC service is available, running, properly configured BEGIN DISTRIBUTED TRANSACTION; DELETE [dbo].[charge] WHERE charge_no = 1; DELETE [JOSEPHSACK-PC\AUGUSTUS].[Credit].[dbo].[charge] WHERE charge_no = 1; ROLLBACK TRANSACTION;
Le TRANSACTION_MUTEX
n'a montré aucune activité sur le serveur local :
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 0 0 0 0
Cependant le nombre de tâches en attente a été incrémenté sur le serveur distant :
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 1 0 0 0
Donc, mon attente de voir cela a été confirmée - étant donné que nous avons une transaction distribuée avec plus d'une session impliquée d'une manière ou d'une autre dans la même transaction.
MARS (ensembles de résultats actifs multiples)
Qu'en est-il de l'utilisation de plusieurs ensembles de résultats actifs (MARS) ? Est-ce que nous nous attendrions également à voir TRANSACTION_MUTEX
s'accumuler lorsqu'il est associé à l'utilisation de MARS ?
Pour cela, j'ai utilisé le code d'application de console C # suivant testé à partir de Microsoft Visual Studio par rapport à mon instance SQL Server 2012 et à la base de données de crédit. La logique de ce que je fais réellement n'est pas très utile (renvoie une ligne de chaque table), mais les lecteurs de données sont sur la même connexion et l'attribut de connexion MultipleActiveResultSets
est défini sur true, il suffisait donc de vérifier si MARS pouvait effectivement piloter TRANSACTION_MUTEX
accumulation aussi :
string ConnString = @"Server=.;Database=Credit;Trusted_Connection=True;MultipleActiveResultSets=true;"; SqlConnection MARSCon = new SqlConnection(ConnString); MARSCon.Open(); SqlCommand MARSCmd1 = new SqlCommand("SELECT payment_no FROM dbo.payment;", MARSCon); SqlCommand MARSCmd2 = new SqlCommand("SELECT charge_no FROM dbo.charge;", MARSCon); SqlDataReader MARSReader1 = MARSCmd1.ExecuteReader(); SqlDataReader MARSReader2 = MARSCmd2.ExecuteReader(); MARSReader1.Read(); MARSReader2.Read(); Console.WriteLine("\t{0}", MARSReader1[0]); Console.WriteLine("\t{0}", MARSReader2[0]);
Après avoir exécuté ce code, j'ai vu l'accumulation suivante pour TRANSACTION_MUTEX
:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 8 2 0 0
Donc, comme vous pouvez le voir, l'activité MARS (même si elle est implémentée de manière triviale) a provoqué une légère hausse dans le TRANSACTION_MUTEX
attente type accumulation. Et l'attribut de chaîne de connexion lui-même ne le pilote pas, l'implémentation réelle le fait. Par exemple, j'ai supprimé l'implémentation du deuxième lecteur et je n'ai maintenu qu'un seul lecteur avec MultipleActiveResultSets=true
, et comme prévu, il n'y avait pas de TRANSACTION_MUTEX
accumulation de temps d'attente.
Conclusion
Si vous voyez un niveau élevé de TRANSACTION_MUTEX
d'attente dans votre environnement, j'espère que cet article vous donnera un aperçu de trois pistes à explorer - pour déterminer à la fois d'où viennent ces attentes et si elles sont nécessaires ou non.