Ostatnio napotkałem wysoki poziom TRANSACTION_MUTEX
skumulowany czas oczekiwania w systemie klienta. Nie pamiętam przypadku, w którym widziałem ten typ oczekiwania na szczycie listy „długich oczekiwań” i byłem ciekawy, jakie czynniki mogą popchnąć ten typ ogólnego czasu oczekiwania.
Definicja TRANSACTION_MUTEX
w Books Online jest to, że „występuje podczas synchronizacji dostępu do transakcji przez wiele partii”. Niewiele obszarów w silniku SQL Server eksponuje tego typu funkcje, więc moje badanie zostało zawężone do następujących technologii:
- Przestarzały
sp_getbindtoken
isp_bindsession
systemowe procedury składowane używane do obsługi połączeń powiązanych - Transakcje rozproszone
- MARS (wiele aktywnych zestawów wyników)
Moim celem było przetestowanie każdej technologii i sprawdzenie, czy ma ona wpływ na TRANSACTION_MUTEX
typ oczekiwania.
Pierwszy test, który przeprowadziłem, używał przestarzałego sp_getbindtoken
i sp_bindsession
procedury składowane. sp_getbindtoken
zwraca identyfikator transakcji, który może być następnie użyty przez sp_bindsession
aby powiązać wiele sesji razem w ramach tej samej transakcji.
Przed każdym scenariuszem testowym wyczyściłem statystyki oczekiwania mojej testowej instancji SQL Server:
DBCC SQLPERF('waitstats', CLEAR); GO
Moja testowa instancja programu SQL Server działała pod kontrolą programu SQL Server 2012 SP1 Developer Edition (11.0.3000). Użyłem przykładowej bazy danych Credit, chociaż możesz użyć dowolnego innego rodzaju przykładowej bazy danych, takiej jak AdventureWorks, jeśli chcesz, ponieważ schemat i dystrybucja danych nie są bezpośrednio związane z tematem tego artykułu i nie były konieczne do prowadzenia TRANSACTION_MUTEX
czas oczekiwania.
sp_getbindtoken / sp_bindsession
W pierwszym oknie sesji SQL Server Management Studio wykonałem następujący kod, aby rozpocząć transakcję i wyprowadzić token wiązania do rejestracji przez inne zaplanowane sesje:
USE Credit; GO BEGIN TRANSACTION; DECLARE @out_token varchar(255); EXECUTE sp_getbindtoken @out_token OUTPUT; SELECT @out_token AS out_token; GO
To zwróciło @out_token
z S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---
. W dwóch oddzielnych oknach zapytań SQL Server Management Studio wykonałem następujący kod, aby dołączyć do istniejących sesji (dostęp do współdzielonej transakcji):
USE Credit; GO EXEC sp_bindsession 'S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---';
A gdy pierwsze okno sesji było nadal otwarte, rozpocząłem następującą pętlę, aby zaktualizować tabelę opłat z datą obciążenia równą bieżącej dacie i godzinie, a następnie wykonałem tę samą logikę w pozostałych dwóch oknach (trzy aktywne sesje w pętla):
WHILE 1 = 1 BEGIN UPDATE dbo.charge SET charge_dt = SYSDATETIME(); END
Po kilku sekundach anulowałem każde wykonywane zapytanie. Z trzech sesji tylko jedna była w stanie faktycznie wykonać aktualizacje — mimo że pozostałe dwie sesje były aktywnie połączone z tą samą transakcją. A gdybym spojrzał na TRANSACTION_MUTEX
typ oczekiwania, widzę, że rzeczywiście się zwiększył:
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';
Wyniki dla tego konkretnego testu były następujące:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 2 181732 93899 0
Widzę więc, że były dwa zadania oczekujące (dwie sesje, które jednocześnie próbowały zaktualizować tę samą tabelę za pomocą pętli). Ponieważ nie wykonałem SET NOCOUNT ON
, mogłem zobaczyć, że tylko pierwsza wykonana UPDATE
pętla wprowadziła zmiany. Wypróbowałem tę podobną technikę, używając kilku różnych odmian (na przykład – cztery ogólne sesje, z trzema czekającymi) – i TRANSACTION_MUTEX
inkrementacja wykazała podobne zachowanie. Widziałem też TRANSACTION_MUTEX
akumulacja podczas jednoczesnej aktualizacji innej tabeli dla każdej sesji – więc modyfikacje tego samego obiektu w pętli nie były wymagane w celu odtworzenia TRANSACTION_MUTEX
kumulacja czasu oczekiwania.
Transakcje rozproszone
Mój następny test polegał na sprawdzeniu, czy TRANSACTION_MUTEX
Zwiększono czas oczekiwania dla transakcji rozproszonych. Do tego testu użyłem dwóch instancji SQL Server i połączonego między nimi serwera połączonego. Usługa MS DTC była uruchomiona i poprawnie skonfigurowana, a ja wykonałem następujący kod, który wykonał lokalne polecenie DELETE
i zdalny DELETE
za pośrednictwem połączonego serwera, a następnie wycofano zmiany:
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
nie wykazał żadnej aktywności na lokalnym serwerze:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 0 0 0 0
Jednak liczba oczekujących zadań została zwiększona na zdalnym serwerze:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 1 0 0 0
Więc moje oczekiwanie, że to się potwierdziło – biorąc pod uwagę, że mamy jedną rozproszoną transakcję z więcej niż jedną sesją zaangażowaną w jakiś sposób w tę samą transakcję.
MARS (wiele aktywnych zestawów wyników)
A co z korzystaniem z wielu aktywnych zestawów wyników (MARS)? Czy spodziewalibyśmy się również zobaczyć TRANSACTION_MUTEX
gromadzić się, gdy jest powiązany z użyciem MARS?
W tym celu użyłem następującego kodu aplikacji konsoli C# przetestowanej z Microsoft Visual Studio na mojej instancji SQL Server 2012 i bazie danych Credit. Logika tego, co faktycznie robię, nie jest zbyt użyteczna (zwraca jeden wiersz z każdej tabeli), ale czytniki danych są na tym samym połączeniu, a atrybut połączenia MultipleActiveResultSets
jest ustawiona na true, więc wystarczyło sprawdzić, czy rzeczywiście MARS może sterować TRANSACTION_MUTEX
również akumulacja:
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 wykonaniu tego kodu zobaczyłem następującą akumulację dla TRANSACTION_MUTEX
:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 8 2 0 0
Jak widać, aktywność MARS (jakkolwiek banalnie zaimplementowana) spowodowała wzrost w TRANSACTION_MUTEX
akumulacja typu oczekiwania. A sam atrybut ciągu połączenia nie steruje tym, robi to rzeczywista implementacja. Na przykład usunąłem drugą implementację czytnika i utrzymywałem tylko jeden czytnik z MultipleActiveResultSets=true
i zgodnie z oczekiwaniami nie było TRANSACTION_MUTEX
kumulacja czasu oczekiwania.
Wniosek
Jeśli widzisz wysoki poziom TRANSACTION_MUTEX
czeka w twoim środowisku, mam nadzieję, że ten post da ci pewien wgląd w trzy sposoby do zbadania - aby określić, skąd te oczekiwania pochodzą i czy są konieczne.