Jag stötte nyligen på hög TRANSACTION_MUTEX
ackumulerad väntetid på ett klientsystem. Jag kunde inte komma ihåg ett fall där jag såg denna väntetyp så nära toppen av "höga väntetider"-listan och jag var nyfiken på vilka faktorer som kunde pressa denna typ av total väntetid.
Books Online-definitionen av TRANSACTION_MUTEX
är att det "uppstår under synkronisering av åtkomst till en transaktion med flera batcher." Det är inte många områden inom SQL Server-motorn som exponerar den här typen av funktionalitet, så min undersökning begränsades till följande teknologier:
- Det föråldrade
sp_getbindtoken
ochsp_bindsession
systemlagrade procedurer som används för att hantera bundna anslutningar - Distribuerade transaktioner
- MARS (flera aktiva resultatuppsättningar)
Mitt mål var att testa varje teknik och se om den påverkade TRANSACTION_MUTEX
vänta typ.
Det första testet jag utförde använde den föråldrade sp_getbindtoken
och sp_bindsession
lagrade procedurer. sp_getbindtoken
returnerar en transaktionsidentifierare som sedan kan användas av sp_bindsession
för att binda samman flera sessioner på samma transaktion.
Före varje testscenario såg jag till att rensa min test SQL Server-instanss väntestatistik:
DBCC SQLPERF('waitstats', CLEAR); GO
Min test-SQL Server-instans körde SQL Server 2012 SP1 Developer Edition (11.0.3000). Jag använde kreditprovdatabasen, även om du kunde använda vilken annan typ av exempeldatabas som helst som AdventureWorks om du ville, eftersom schemat och datadistributionen inte är direkt relevant för ämnet i denna artikel och inte var nödvändig för att köra TRANSACTION_MUTEX
väntetid.
sp_getbindtoken / sp_bindsession
I det första sessionsfönstret i SQL Server Management Studio körde jag följande kod för att påbörja en transaktion och mata ut bindstokenet för värvning av de andra planerade sessionerna:
USE Credit; GO BEGIN TRANSACTION; DECLARE @out_token varchar(255); EXECUTE sp_getbindtoken @out_token OUTPUT; SELECT @out_token AS out_token; GO
Detta returnerade en @out_token
av S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---
. I två separata frågefönster för SQL Server Management Studio körde jag följande kod för att ansluta till befintliga sessioner (åtkomst till den delade transaktionen):
USE Credit; GO EXEC sp_bindsession 'S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---';
Och med det första sessionsfönstret fortfarande öppet startade jag följande loop för att uppdatera laddningstabellens tabell med ett laddningsdatum lika med det aktuella datumet och tiden, och körde sedan samma logik i de andra två fönstren (tre aktiva sessioner i loop):
WHILE 1 = 1 BEGIN UPDATE dbo.charge SET charge_dt = SYSDATETIME(); END
Efter några sekunder avbröt jag varje körande fråga. Av de tre sessionerna kunde bara en faktiskt utföra uppdateringar – även om de andra två sessionerna aktivt kopplades till samma transaktion. Och om jag tittade på TRANSACTION_MUTEX
vänta typ, jag kan se att det verkligen ökade:
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';
Resultaten för just detta test var följande:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 2 181732 93899 0
Så jag ser att det fanns två väntande uppgifter (de två sessioner som samtidigt försökte uppdatera samma tabell via slingan). Eftersom jag inte hade kört SET NOCOUNT ON
, jag kunde se att endast den första UPDATE
körde loop fick ändringar. Jag provade den här liknande tekniken med några olika varianter (till exempel – fyra övergripande sessioner, med tre väntande) – och TRANSACTION_MUTEX
ökande visade liknande beteende. Jag såg också TRANSACTION_MUTEX
ackumulering när man samtidigt uppdaterar en annan tabell för varje session – så ändringar mot samma objekt i en loop krävdes inte för att reproducera TRANSACTION_MUTEX
väntetidsackumulering.
Distribuerade transaktioner
Mitt nästa test innebar att se om TRANSACTION_MUTEX
väntetiden ökades för distribuerade transaktioner. För det här testet använde jag två SQL Server-instanser och en länkad server ansluten mellan de två. MS DTC kördes och var korrekt konfigurerad, och jag körde följande kod som utförde en lokal DELETE
och en fjärrkontroll DELETE
via den länkade servern och återställde sedan ändringarna:
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;
TRANSACTION_MUTEX
visade ingen aktivitet på den lokala servern:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 0 0 0 0
Men antalet väntande uppgifter ökades på fjärrservern:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 1 0 0 0
Så min förväntning att se detta bekräftades – med tanke på att vi har en distribuerad transaktion med mer än en session involverad på något sätt med samma transaktion.
MARS (flera aktiva resultatuppsättningar)
Hur är det med användningen av flera aktiva resultatuppsättningar (MARS)? Skulle vi också förvänta oss att se TRANSACTION_MUTEX
ackumuleras när det är kopplat till MARS-användning?
För detta använde jag följande C#-konsolapplikationskod testad från Microsoft Visual Studio mot min SQL Server 2012-instans och Credit-databasen. Logiken i det jag faktiskt gör är inte särskilt användbar (returnerar en rad från varje tabell), men dataläsarna finns på samma anslutning och anslutningsattributet MultipleActiveResultSets
är satt till sant, så det räckte för att verifiera om MARS verkligen kunde köra TRANSACTION_MUTEX
ackumulering också:
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]);
Efter att ha kört den här koden såg jag följande ackumulering för TRANSACTION_MUTEX
:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 8 2 0 0
Så som du kan se, orsakade MARS-aktiviteten (hur trivialt än implementerad) en ökning i TRANSACTION_MUTEX
vänta typ ackumulering. Och anslutningssträngsattributet i sig driver inte detta, det gör den faktiska implementeringen. Till exempel tog jag bort den andra läsarimplementeringen och behöll bara en enda läsare med MultipleActiveResultSets=true
, och som förväntat fanns det ingen TRANSACTION_MUTEX
väntetidsackumulering.
Slutsats
Om du ser hög TRANSACTION_MUTEX
väntar i din miljö, jag hoppas att det här inlägget ger dig lite insikt i tre vägar att utforska - för att avgöra både var dessa väntan kommer ifrån och om de är nödvändiga eller inte.