Ik heb onlangs last van hoge TRANSACTION_MUTEX
geaccumuleerde wachttijd op een clientsysteem. Ik kon me geen geval herinneren waarin ik dit type wachttijd bovenaan de lijst met "hoge wachttijden" zag staan en ik was benieuwd welke factoren dit type algemene wachttijd zouden kunnen opdrijven.
De Books Online-definitie van TRANSACTION_MUTEX
is dat het "optreedt tijdens synchronisatie van toegang tot een transactie door meerdere batches." Niet veel gebieden binnen de SQL Server-engine stellen dit soort functionaliteit bloot, dus mijn onderzoek werd beperkt tot de volgende technologieën:
- Het verouderde
sp_getbindtoken
ensp_bindsession
door het systeem opgeslagen procedures die worden gebruikt om gebonden verbindingen af te handelen - Gedistribueerde transacties
- MARS (meerdere actieve resultatensets)
Mijn doel was om elke technologie te testen en te zien of deze de TRANSACTION_MUTEX
. beïnvloedde wacht type.
De eerste test die ik uitvoerde gebruikte het verouderde sp_getbindtoken
en sp_bindsession
opgeslagen procedures. Het sp_getbindtoken
geeft een transactie-ID terug die vervolgens kan worden gebruikt door sp_bindsession
om meerdere sessies samen te binden op dezelfde transactie.
Voor elk testscenario heb ik ervoor gezorgd dat de wachtstatistieken van mijn SQL Server-testinstantie zijn gewist:
DBCC SQLPERF('waitstats', CLEAR); GO
Op mijn testexemplaar van SQL Server werd SQL Server 2012 SP1 Developer Edition (11.0.3000) uitgevoerd. Ik heb de Credit-voorbeelddatabase gebruikt, hoewel je desgewenst elk ander soort voorbeelddatabase zoals AdventureWorks kunt gebruiken, omdat het schema en de gegevensdistributie niet direct relevant zijn voor het onderwerp van dit artikel en niet nodig waren om te rijden de TRANSACTION_MUTEX
wachttijd.
sp_getbindtoken / sp_bindsession
In het eerste sessievenster van SQL Server Management Studio heb ik de volgende code uitgevoerd om een transactie te starten en het bindtoken uit te voeren voor aanmelding door de andere geplande sessies:
USE Credit; GO BEGIN TRANSACTION; DECLARE @out_token varchar(255); EXECUTE sp_getbindtoken @out_token OUTPUT; SELECT @out_token AS out_token; GO
Dit leverde een @out_token
. op van S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---
. In twee afzonderlijke queryvensters van SQL Server Management Studio heb ik de volgende code uitgevoerd om deel te nemen aan de bestaande sessies (toegang tot de gedeelde transactie):
USE Credit; GO EXEC sp_bindsession 'S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---';
En met het eerste sessievenster nog steeds open, begon ik de volgende lus om de tabel van de oplaadtabel bij te werken met een oplaaddatum gelijk aan de huidige datum en tijd, en voerde vervolgens dezelfde logica uit in de andere twee vensters (drie actieve sessies in de lus):
WHILE 1 = 1 BEGIN UPDATE dbo.charge SET charge_dt = SYSDATETIME(); END
Na een paar seconden heb ik elke uitvoerende query geannuleerd. Van de drie sessies was er slechts één in staat om daadwerkelijk updates uit te voeren, ook al waren de andere twee sessies actief betrokken bij dezelfde transactie. En als ik keek naar de TRANSACTION_MUTEX
wacht type, ik kan zien dat het inderdaad is toegenomen:
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';
De resultaten voor deze specifieke test waren als volgt:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 2 181732 93899 0
Dus ik zie dat er twee wachttaken waren (de twee sessies die tegelijkertijd probeerden dezelfde tabel via de lus bij te werken). Aangezien ik SET NOCOUNT ON
niet had uitgevoerd , ik kon zien dat alleen de eerste uitgevoerde UPDATE
loop kreeg veranderingen. Ik probeerde deze vergelijkbare techniek met een paar verschillende variaties (bijvoorbeeld – vier algemene sessies, met drie wachten) – en de TRANSACTION_MUTEX
toename vertoonde vergelijkbaar gedrag. Ik zag ook de TRANSACTION_MUTEX
accumulatie bij het gelijktijdig updaten van een andere tabel voor elke sessie - dus aanpassingen aan hetzelfde object in een lus waren niet nodig om de TRANSACTION_MUTEX
te reproduceren wachttijd accumulatie.
Gedistribueerde transacties
Mijn volgende test was om te kijken of TRANSACTION_MUTEX
wachttijd werd verhoogd voor gedistribueerde transacties. Voor deze test heb ik twee SQL Server-instanties en een gekoppelde server tussen beide gebruikt. MS DTC was actief en correct geconfigureerd, en ik voerde de volgende code uit die een lokale DELETE
uitvoerde en een afstandsbediening DELETE
via de gekoppelde server en vervolgens de wijzigingen teruggedraaid:
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;
De TRANSACTION_MUTEX
toonde geen activiteit op de lokale server:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 0 0 0 0
Het aantal wachtende taken is echter verhoogd op de externe server:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 1 0 0 0
Dus mijn verwachting om dit te zien werd bevestigd - aangezien we één gedistribueerde transactie hebben met meer dan één sessie die op de een of andere manier bij dezelfde transactie betrokken is.
MARS (meerdere actieve resultatensets)
Hoe zit het met het gebruik van Multiple Active Result Sets (MARS)? Zouden we ook TRANSACTION_MUTEX
. verwachten? accumuleren wanneer geassocieerd met MARS-gebruik?
Hiervoor heb ik de volgende C#-consoletoepassingscode gebruikt die is getest door Microsoft Visual Studio tegen mijn SQL Server 2012-instantie en de Credit-database. De logica van wat ik eigenlijk doe is niet erg handig (retourneert één rij van elke tabel), maar de gegevenslezers hebben dezelfde verbinding en het verbindingskenmerk MultipleActiveResultSets
is ingesteld op true, dus het was voldoende om te verifiëren of MARS inderdaad TRANSACTION_MUTEX
kon aansturen ook accumulatie:
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]);
Na het uitvoeren van deze code, zag ik de volgende accumulatie voor TRANSACTION_MUTEX
:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 8 2 0 0
Zoals je kunt zien, veroorzaakte de MARS-activiteit (hoe triviaal ook geïmplementeerd) een stijging in de TRANSACTION_MUTEX
wacht type accumulatie. En het kenmerk van de verbindingsreeks zelf stuurt dit niet aan, de daadwerkelijke implementatie wel. Ik heb bijvoorbeeld de implementatie van de tweede lezer verwijderd en slechts één lezer behouden met MultipleActiveResultSets=true
, en zoals verwacht, was er geen TRANSACTION_MUTEX
wachttijd accumulatie.
Conclusie
Als u hoge TRANSACTION_MUTEX
. ziet wacht in uw omgeving, ik hoop dat dit bericht u enig inzicht geeft in drie wegen die u kunt verkennen - om te bepalen waar deze wachttijden vandaan komen en of ze al dan niet nodig zijn.