mogę może również odtworzyć to w 100% na moim komputerze. (patrz uwaga na końcu)
Istotą problemu jest to, że wyjmujesz S
blokuje wiersze tabeli systemowej w tempdb
które mogą kolidować z blokadami potrzebnymi do wewnętrznego tempdb
transakcje czyszczenia.
Gdy ta praca porządkowa jest przydzielona do tej samej sesji, która jest właścicielem S
może nastąpić zawieszenie się na czas nieokreślony.
Aby na pewno uniknąć tego problemu, musisz przestać odwoływać się do system
obiekty wewnątrz tempdb
.
Możliwe jest utworzenie tabeli liczb bez odwoływania się w ogóle do tabel zewnętrznych. Następujące elementy nie muszą czytać wierszy tabeli podstawowej, a zatem również nie wymagają blokad.
WITH Ten(N) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
SELECT TOP 1000000 IDENTITY(INT, 1, 1) Number
INTO Numbers
FROM Ten T10,
Ten T100,
Ten T1000,
Ten T10000,
Ten T100000,
Ten T1000000
Kroki do odtworzenia
Najpierw utwórz procedurę
CREATE PROC P
AS
SET NOCOUNT ON;
DECLARE @T TABLE (X INT)
GO
Następnie uruchom ponownie usługę SQL i w jednym połączeniu wykonaj
WHILE NOT EXISTS(SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id)
BEGIN
/*This will cause the problematic droptemp transactions*/
EXEC sp_recompile 'P'
EXEC P
END;
SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id
Następnie uruchom inne połączenie
USE tempdb;
SELECT TOP 1000000 IDENTITY(INT, 1, 1) Number
INTO #T
FROM sys.objects s1
CROSS JOIN sys.objects s2
CROSS JOIN sys.objects s3
CROSS JOIN sys.objects s4;
DROP TABLE #T
Zapytanie wypełniające tabelę Numbers wydaje się być w stanie wejść w sytuację blokady na żywo z wewnętrznymi transakcjami systemowymi, które czyszczą obiekty tymczasowe, takie jak zmienne tabeli.
Udało mi się w ten sposób zablokować identyfikator sesji 53. Jest zablokowany na czas nieokreślony. Dane wyjściowe sp_WhoIsActive
pokazuje, że ten pająk prawie cały czas spędza w zawieszeniu. W kolejnych przebiegach liczby w reads
kolumna wzrasta, ale wartości w pozostałych kolumnach pozostają w dużej mierze takie same.
Czas oczekiwania nie wykazuje rosnącego wzorca, ale wskazuje, że musi być okresowo odblokowywany, zanim zostanie ponownie zablokowany.
SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id
Zwroty
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
| waiting_task_address | session_id | exec_context_id | wait_duration_ms | wait_type | resource_address | blocking_task_address | blocking_session_id | blocking_exec_context_id | resource_description |
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
| 0x00000002F2C170C8 | 53 | 0 | 86 | LCK_M_X | 0x00000002F9B13040 | 0x00000002F2C170C8 | 53 | NULL | keylock hobtid=281474978938880 dbid=2 id=lock2f9ac8880 mode=U associatedObjectId=281474978938880 |
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
Korzystanie z identyfikatora w opisie zasobu
SELECT o.name
FROM sys.allocation_units au WITH (NOLOCK)
INNER JOIN sys.partitions p WITH (NOLOCK)
ON au.container_id = p.partition_id
INNER JOIN sys.all_objects o WITH (NOLOCK)
ON o.object_id = p.object_id
WHERE allocation_unit_id = 281474978938880
Zwroty
+------------+
| name |
+------------+
| sysschobjs |
+------------+
Bieganie
SELECT resource_description,request_status
FROM sys.dm_tran_locks
WHERE request_session_id = 53 AND request_status <> 'GRANT'
Zwroty
+----------------------+----------------+
| resource_description | request_status |
+----------------------+----------------+
| (246708db8c1f) | CONVERT |
+----------------------+----------------+
Podłączanie przez DAC i uruchamianie
SELECT id,name
FROM tempdb.sys.sysschobjs WITH (NOLOCK)
WHERE %%LOCKRES%% = '(246708db8c1f)'
Zwroty
+-------------+-----------+
| id | name |
+-------------+-----------+
| -1578606288 | #A1E86130 |
+-------------+-----------+
Ciekawe, co to jest
SELECT name,user_type_id
FROM tempdb.sys.columns
WHERE object_id = -1578606288
Zwroty
+------+--------------+
| name | user_type_id |
+------+--------------+
| X | 56 |
+------+--------------+
To jest nazwa kolumny w zmiennej tabeli używanej przez przechowywaną procedurę.
Bieganie
SELECT request_mode,
request_status,
request_session_id,
request_owner_id,
lock_owner_address,
t.transaction_id,
t.name,
t.transaction_begin_time
FROM sys.dm_tran_locks l
JOIN sys.dm_tran_active_transactions t
ON l.request_owner_id = t.transaction_id
WHERE resource_description = '(246708db8c1f)'
Zwroty
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| request_mode | request_status | request_session_id | request_owner_id | lock_owner_address | transaction_id | name | transaction_begin_time |
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| U | GRANT | 53 | 227647 | 0x00000002F1EF6800 | 227647 | droptemp | 2013-11-24 18:36:28.267 |
| S | GRANT | 53 | 191790 | 0x00000002F9B16380 | 191790 | SELECT INTO | 2013-11-24 18:21:30.083 |
| X | CONVERT | 53 | 227647 | 0x00000002F9B12FC0 | 227647 | droptemp | 2013-11-24 18:36:28.267 |
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
Więc SELECT INTO
transakcja zawiera S
zablokuj wiersz w tempdb.sys.sysschobjs
odnoszące się do zmiennej tabeli #A1E86130
. droptemp
transakcja nie może uzyskać X
zablokować ten wiersz z powodu konfliktu S
zablokuj.
Wielokrotne uruchamianie tego zapytania pokazuje, że transaction_id
dla droptemp
transakcja wielokrotnie się zmienia.
Spekuluję, że SQL Server musi alokować te wewnętrzne transakcje do spidów użytkowników i nadawać im priorytety przed wykonaniem pracy z użytkownikiem. Tak więc identyfikator sesji 53 utknął w stałym cyklu, w którym uruchamia droptemp
transakcja, jest blokowana przez transakcję użytkownika działającą na tym samym spid. Wycofuje wewnętrzną transakcję, a następnie powtarza proces w nieskończoność.
Potwierdza to śledzenie różnych zdarzeń blokowania i transakcji w SQL Server Profiler po zawieszeniu się spid.
Śledziłem również wcześniejsze zdarzenia blokowania.
Zablokuj blokowanie zdarzeń
Większość wspólnych blokad kluczy usuwanych przez SELECT INTO
transakcja na kluczach w sysschobjs
zostać natychmiast zwolniony. Wyjątkiem jest pierwsza blokada na (246708db8c1f)
.
Ma to sens, ponieważ plan pokazuje skany zagnieżdżonych pętli [sys].[sysschobjs].[clst] [o]
a ponieważ obiekty tymczasowe otrzymują ujemne identyfikatory obiektów, będą pierwszymi wierszami napotkanymi w kolejności skanowania.
Zetknąłem się również z sytuacją opisaną w PO, gdzie uruchomienie trójstronnego łączenia krzyżowego jako pierwsze wydaje się pozwolić na odniesienie sukcesu czterokierunkowego.
Kilka pierwszych zdarzeń w śladzie dla SELECT INTO
transakcja ma zupełnie inny wzór.
Było to po ponownym uruchomieniu usługi, więc wartości zasobów blokady w kolumnie danych tekstowych nie są bezpośrednio porównywalne.
Zamiast zachowywać blokadę na pierwszym kluczu, a następnie wzorzec zdobywania i zwalniania kolejnych kluczy, wydaje się, że uzyskuje znacznie więcej zamków bez ich początkowego zwalniania.
Zakładam, że musi istnieć jakaś rozbieżność w strategii wykonania, która pozwoli uniknąć problemu.
Aktualizacja
Zgłoszony przeze mnie element Connect o tym
nie został oznaczony jako naprawiony, ale obecnie korzystam z dodatku SP2 dla programu SQL Server 2012 i mogę teraz odtwarzać tylko tymczasowe samoblokowanie, a nie stałe. Nadal otrzymuję samoblokowanie, ale po pewnej liczbie nieudanych prób wykonania droptemp
transakcja pomyślnie wydaje się, że wraca do przetwarzania transakcji użytkownika. Po tym zatwierdza transakcja systemowa, a następnie jest wykonywana pomyślnie. Wciąż na tym samym spidzie. (osiem prób w jednym przykładowym przebiegu. Nie jestem pewien, czy będzie to powtarzane konsekwentnie)