sql >> Databasteknik >  >> RDS >> Access

TRANSACTION_MUTEX och transaktionsåtkomst för flera sessioner

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 och sp_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.


  1. Med sqlalchemy hur man dynamiskt binder till databasmotorn per begäran

  2. Hur ändrar man en databas till postgresql med Symfony 2.0?

  3. ResultSet#getDate() semantik

  4. Hur kontrollerar du om IDENTITY_INSERT är inställt på PÅ eller AV i SQL Server?