Nedávno jsem narazil na vysoký TRANSACTION_MUTEX
akumulovaná doba čekání na klientském systému. Nepamatuji si případ, kdy bych viděl tento typ čekání jako blízko vrcholu seznamu „vysokých čekací“ a byl jsem zvědavý, jaké faktory mohou tento typ celkové čekací doby posunout.
Definice TRANSACTION_MUTEX
v Books Online je, že "dochází během synchronizace přístupu k transakci pomocí více dávek." Tento typ funkcí nevykazuje mnoho oblastí v enginu SQL Serveru, takže moje šetření se zúžilo na následující technologie:
- Zastaralý
sp_getbindtoken
asp_bindsession
systémové uložené procedury používané ke zpracování vázaných připojení - Distribuované transakce
- MARS (Multiple Active Result Sets)
Mým cílem bylo otestovat každou technologii a zjistit, zda ovlivnila TRANSACTION_MUTEX
typ čekání.
První test, který jsem provedl, použil zastaralý sp_getbindtoken
a sp_bindsession
uložené procedury. sp_getbindtoken
vrátí identifikátor transakce, který pak může použít sp_bindsession
pro svázání více relací na stejnou transakci.
Před každým testovacím scénářem jsem se ujistil, že jsem vymazal statistiku čekání své testovací instance SQL Server:
DBCC SQLPERF('waitstats', CLEAR);GO
Moje testovací instance SQL Server používala SQL Server 2012 SP1 Developer Edition (11.0.3000). Použil jsem vzorovou databázi kreditu, i když byste mohli použít jakýkoli jiný druh vzorové databáze, jako je AdventureWorks, pokud byste chtěli, protože schéma a distribuce dat přímo nesouvisí s předmětem tohoto článku a nebylo nutné k řízení TRANSACTION_MUTEX
čekací doba.
sp_getbindtoken / sp_bindsession
V prvním okně relace SQL Server Management Studio jsem provedl následující kód pro zahájení transakce a výstup tokenu vazby pro zařazení do dalších plánovaných relací:
POUŽÍVEJTE kredit;ZAČNĚTE TRANSAKCI; DECLARE @out_token varchar(255); EXECUTE sp_getbindtoken @out_token OUTPUT; SELECT @out_token AS out_token;GO
To vrátilo @out_token
z S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---
. Ve dvou samostatných oknech dotazu SQL Server Management Studio jsem provedl následující kód pro připojení k existujícím relacím (přístup ke sdílené transakci):
USE Credit;GO EXEC sp_bindsession 'S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---';
A když bylo okno první relace stále otevřené, spustil jsem následující smyčku, abych aktualizoval tabulku účtování poplatků s datem účtování rovným aktuálnímu datu a času, a pak jsem provedl stejnou logiku v dalších dvou oknech (tři aktivní relace v smyčka):
WHILE 1 =1 ZAČÁTE AKTUALIZOVAT dbo.charge SET charge_dt =SYSDATETIME();END
Po několika sekundách jsem každý provádějící dotaz zrušil. Ze tří relací byla pouze jedna schopna skutečně provádět aktualizace – i když další dvě relace byly aktivně připojeny ke stejné transakci. A když jsem se podíval na TRANSACTION_MUTEX
wait type, vidím, že se skutečně zvýšil:
SELECT [wait_type], [waiting_tasks_count], [wait_time_ms], [max_wait_time_ms], [signal_wait_time_ms]FROM sys.dm_os_wait_statsWHERE wait_type ='TRANSACTION_MUTEX';
Výsledky tohoto konkrétního testu byly následující:
wait_type wait_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_msTRANSACTION_MUTEX 2 181732 93899 0
Takže vidím, že tam byly dva čekající úkoly (dvě relace, které se současně pokoušely aktualizovat stejnou tabulku přes smyčku). Protože jsem neprovedl SET NOCOUNT ON
, byl jsem schopen vidět, že pouze první provedené UPDATE
smyčka prošla změnami. Zkoušel jsem tuto podobnou techniku pomocí několika různých variant (například – čtyři celkové relace, tři čekání) – a TRANSACTION_MUTEX
přírůstek vykazoval podobné chování. Také jsem viděl TRANSACTION_MUTEX
akumulace při současné aktualizaci jiné tabulky pro každou relaci – takže úpravy proti stejnému objektu ve smyčce nebyly nutné k reprodukci TRANSACTION_MUTEX
akumulace čekací doby.
Distribuované transakce
Můj další test zahrnoval zjištění, zda TRANSACTION_MUTEX
čekací doba byla zvýšena pro distribuované transakce. Pro tento test jsem použil dvě instance SQL Server a propojený server připojený mezi nimi. MS DTC byl spuštěn a správně nakonfigurován a provedl jsem následující kód, který provedl místní DELETE
a vzdálené DELETE
přes propojený server a poté vrátil změny:
USE Credit;GO SET XACT_ABORT ON; -- Předpokládá, že služba MS DTC je dostupná, běžící, správně nakonfigurovanáZAČÁTEK DISTRIBUOVANÉ TRANSAKCE; DELETE [dbo].[charge] WHERE charge_no =1;DELETE [JOSEPHSACK-PC\AUGUSTUS].[Kredit].[dbo].[charge] WHERE charge_no =1; TRANSAKCE VRÁCENÍ;
TRANSACTION_MUTEX
nevykazoval žádnou aktivitu na místním serveru:
wait_type wait_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_msTRANSACTION_MUTEX 0 0 0 0
Počet čekajících úloh byl však na vzdáleném serveru zvýšen:
wait_type wait_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_msTRANSACTION_MUTEX 1 0 0 0
Moje očekávání, že to uvidím, se tedy potvrdilo – vzhledem k tomu, že máme jednu distribuovanou transakci s více než jednou relací, která je nějakým způsobem zapojena do stejné transakce.
MARS (Multiple Active Result Sets)
A co použití Multiple Active Result Sets (MARS)? Očekávali bychom také, že uvidíme TRANSACTION_MUTEX
akumulovat, když je spojeno s používáním MARS?
K tomu jsem použil následující kód konzolové aplikace C# testovaný z Microsoft Visual Studio proti mé instanci SQL Server 2012 a databázi Credit. Logika toho, co ve skutečnosti dělám, není příliš užitečná (vrací jeden řádek z každé tabulky), ale čtečky dat jsou na stejném připojení a atribut připojení MultipleActiveResultSets
je nastaveno na true, takže stačilo ověřit, zda skutečně MARS dokáže řídit TRANSACTION_MUTEX
akumulace také:
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]);
Po provedení tohoto kódu jsem viděl následující nahromadění pro TRANSACTION_MUTEX
:
wait_type wait_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_msTRANSACTION_MUTEX 8 2 0 0
Jak tedy vidíte, aktivita MARS (jakkoli triviálně implementovaná) způsobila nárůst v TRANSACTION_MUTEX
akumulace typu čekání. A samotný atribut spojovacího řetězce to neřídí, skutečná implementace ano. Například jsem odstranil implementaci druhé čtečky a pouze jsem udržoval jednu čtečku s MultipleActiveResultSets=true
a podle očekávání nedošlo k žádnému TRANSACTION_MUTEX
akumulace čekací doby.
Závěr
Pokud vidíte vysokou hodnotu TRANSACTION_MUTEX
čekání ve vašem prostředí, doufám, že vám tento příspěvek poskytne náhled do tří cest, které je třeba prozkoumat - abyste zjistili, odkud tato čekání pocházejí a zda jsou nebo nejsou nezbytná.