Jeg stødte for nylig på høj TRANSACTION_MUTEX
akkumuleret ventetid på et klientsystem. Jeg kunne ikke huske et tilfælde, hvor jeg så denne ventetype som tæt på toppen af listen over "høje ventetider", og jeg var nysgerrig efter, hvilke faktorer der kunne presse denne type overordnet ventetid.
Books Online-definitionen af TRANSACTION_MUTEX
er, at det "opstår under synkronisering af adgang til en transaktion med flere batches." Ikke mange områder inden for SQL Server-motoren afslører denne type funktionalitet, så min undersøgelse blev indsnævret til følgende teknologier:
- Det forældede
sp_getbindtoken
ogsp_bindsession
systemlagrede procedurer, der bruges til at håndtere bundne forbindelser - Distribuerede transaktioner
- MARS (flere aktive resultatsæt)
Mit mål var at teste hver teknologi og se, om den påvirkede TRANSACTION_MUTEX
vent type.
Den første test, jeg udførte, brugte den forældede sp_getbindtoken
og sp_bindsession
lagrede procedurer. sp_getbindtoken
returnerer et transaktions-id, som derefter kan bruges af sp_bindsession
at binde flere sessioner sammen på den samme transaktion.
Før hvert testscenarie sørgede jeg for at rydde min test SQL Server-forekomsts ventestatistikker:
DBCC SQLPERF('waitstats', CLEAR); GO
Min test SQL Server-instans kørte SQL Server 2012 SP1 Developer Edition (11.0.3000). Jeg brugte kreditprøvedatabasen, selvom du kunne bruge enhver anden form for prøvedatabase som AdventureWorks, hvis du ville, da skemaet og datafordelingen ikke er direkte relevant for emnet for denne artikel og ikke var nødvendig for at køre TRANSACTION_MUTEX
ventetid.
sp_getbindtoken / sp_bindsession
I det første sessionsvindue i SQL Server Management Studio udførte jeg følgende kode for at starte en transaktion og udlæse bindetokenet til optagelse af de andre planlagte sessioner:
USE Credit; GO BEGIN TRANSACTION; DECLARE @out_token varchar(255); EXECUTE sp_getbindtoken @out_token OUTPUT; SELECT @out_token AS out_token; GO
Dette returnerede en @out_token
af S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---
. I to separate SQL Server Management Studio-forespørgselsvinduer udførte jeg følgende kode for at deltage i de eksisterende sessioner (adgang til den delte transaktion):
USE Credit; GO EXEC sp_bindsession 'S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---';
Og med det første sessionsvindue stadig åbent, startede jeg den følgende løkke for at opdatere ladetabellens tabel med en opladningsdato svarende til den aktuelle dato og klokkeslæt og udførte derefter den samme logik i de to andre vinduer (tre aktive sessioner i loop):
WHILE 1 = 1 BEGIN UPDATE dbo.charge SET charge_dt = SYSDATETIME(); END
Efter et par sekunder annullerede jeg hver eksekverende forespørgsel. Af de tre sessioner var det kun én, der rent faktisk var i stand til at udføre opdateringer - selvom de to andre sessioner var aktivt tilsluttet den samme transaktion. Og hvis jeg så på TRANSACTION_MUTEX
vent type, jeg kan se, at det faktisk steg:
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';
Resultaterne for denne særlige test var som følger:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 2 181732 93899 0
Så jeg kan se, at der var to ventende opgaver (de to sessioner, der samtidig forsøgte at opdatere den samme tabel via løkken). Da jeg ikke havde udført SET NOCOUNT ON
, jeg kunne se, at kun den første udførte UPDATE
loop fik ændringer. Jeg prøvede denne lignende teknik ved at bruge et par forskellige variationer (for eksempel – fire overordnede sessioner, med tre ventende) – og TRANSACTION_MUTEX
stigende viste lignende adfærd. Jeg så også TRANSACTION_MUTEX
akkumulering ved samtidig opdatering af en anden tabel for hver session – så ændringer mod det samme objekt i en loop var ikke påkrævet for at reproducere TRANSACTION_MUTEX
ventetidsakkumulering.
Distribuerede transaktioner
Min næste test involverede at se, om TRANSACTION_MUTEX
ventetiden blev øget for distribuerede transaktioner. Til denne test brugte jeg to SQL Server-instanser og en forbundet server forbundet mellem de to af dem. MS DTC kørte og var korrekt konfigureret, og jeg udførte følgende kode, der udførte en lokal DELETE
og en ekstern DELETE
via den linkede server og rullede derefter ændringerne tilbage:
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
viste ingen aktivitet på den lokale server:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 0 0 0 0
Men antallet af ventende opgaver blev øget på fjernserveren:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 1 0 0 0
Så min forventning om at se dette blev bekræftet – givet at vi har én distribueret transaktion med mere end én session involveret på en eller anden måde med den samme transaktion.
MARS (Flere aktive resultatsæt)
Hvad med brugen af Multiple Active Result Sets (MARS)? Ville vi også forvente at se TRANSACTION_MUTEX
akkumuleres, når det er forbundet med MARS-brug?
Til dette brugte jeg følgende C#-konsolapplikationskode testet fra Microsoft Visual Studio mod min SQL Server 2012-instans og Credit-databasen. Logikken i det, jeg faktisk laver, er ikke særlig nyttig (returnerer en række fra hver tabel), men datalæserne er på den samme forbindelse og forbindelsesattributten MultipleActiveResultSets
er sat til sand, så det var nok til at bekræfte, om MARS faktisk kunne køre TRANSACTION_MUTEX
også akkumulering:
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 at have udført denne kode, så jeg følgende akkumulering for 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, forårsagede MARS-aktiviteten (dog trivielt implementeret) en stigning i TRANSACTION_MUTEX
ventetype akkumulering. Og selve forbindelsesstrengattributten driver ikke dette, det gør den faktiske implementering. For eksempel fjernede jeg den anden læserimplementering og vedligeholdt bare en enkelt læser med MultipleActiveResultSets=true
, og som forventet var der ingen TRANSACTION_MUTEX
ventetidsakkumulering.
Konklusion
Hvis du ser høj TRANSACTION_MUTEX
venter i dit miljø, håber jeg, at dette indlæg giver dig et indblik i tre muligheder for at udforske - for at bestemme både, hvor disse ventetider kommer fra, og om de er nødvendige eller ej.