Jag kan kunde också återge detta 100% av tiden på min maskin. (se not i slutet)
Kärnan i problemet är att du tar ut S
låser på systemtabellrader i tempdb
som kan komma i konflikt med de lås som behövs för intern tempdb
saneringstransaktioner.
När detta saneringsarbete allokeras till samma session som äger S
lås ett obestämt häng kan inträffa.
För att undvika detta problem med säkerhet måste du sluta referera till system
objekt inuti tempdb
.
Det är möjligt att skapa en taltabell utan att referera till några externa tabeller alls. Följande behöver inte läsa några bastabellrader och tar därför inte heller några lås.
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
Steg för att återskapa
Skapa först en procedur
CREATE PROC P
AS
SET NOCOUNT ON;
DECLARE @T TABLE (X INT)
GO
Starta sedan om SQL Service och kör i en anslutning
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
Kör sedan i en annan anslutning
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
Frågan som fyller i Numbers-tabellen verkar lyckas hamna i en livelåssituation med de interna systemtransaktionerna som rensar upp tillfälliga objekt som tabellvariabler.
Jag lyckades få sessions-id 53 blockerad på detta sätt. Den är blockerad på obestämd tid. Utdata från sp_WhoIsActive
visar att denna spindel tillbringar nästan hela tiden avstängd. I på varandra följande körningar siffrorna i reads
kolumnen ökar men värdena i de andra kolumnerna förblir i stort sett desamma.
Väntetiden visar inte ett ökande mönster men indikerar att den måste avblockeras med jämna mellanrum innan den blockeras igen.
SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id
Retur
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
| 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 |
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
Använda id:t i resursbeskrivningen
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
Retur
+------------+
| name |
+------------+
| sysschobjs |
+------------+
Kör
SELECT resource_description,request_status
FROM sys.dm_tran_locks
WHERE request_session_id = 53 AND request_status <> 'GRANT'
Retur
+----------------------+----------------+
| resource_description | request_status |
+----------------------+----------------+
| (246708db8c1f) | CONVERT |
+----------------------+----------------+
Ansluter via DAC och kör
SELECT id,name
FROM tempdb.sys.sysschobjs WITH (NOLOCK)
WHERE %%LOCKRES%% = '(246708db8c1f)'
Retur
+-------------+-----------+
| id | name |
+-------------+-----------+
| -1578606288 | #A1E86130 |
+-------------+-----------+
Nyfiken på vad det är
SELECT name,user_type_id
FROM tempdb.sys.columns
WHERE object_id = -1578606288
Retur
+------+--------------+
| name | user_type_id |
+------+--------------+
| X | 56 |
+------+--------------+
Detta är kolumnnamnet i tabellvariabeln som används av den lagrade proc.
Kör
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)'
Retur
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| 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 |
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
Så SELECT INTO
transaktionen innehåller en S
lås på raden i tempdb.sys.sysschobjs
som hänför sig till tabellvariabeln #A1E86130
. droptemp
transaktionen kan inte få ett X
lås på den här raden på grund av denna konflikt S
lås.
Att köra den här frågan upprepade gånger visar att transaction_id
för droptemp
transaktionen ändras upprepade gånger.
Jag spekulerar i att SQL Server måste allokera dessa interna transaktioner på användarens spids och prioritera dem innan användararbetet utförs. Så sessions-id 53 har fastnat i en konstant cykel där den startar upp en droptemp
transaktion, blockeras av användartransaktionen som körs på samma spid. Återställer den interna transaktionen och upprepar sedan processen på obestämd tid.
Detta bekräftas genom att spåra de olika låsnings- och transaktionshändelserna i SQL Server Profiler efter att spiden har hängt sig.
Jag spårade även låsningshändelserna innan dess.
Lås händelseblockering
De flesta av de delade nyckellåsen tas ut av SELECT INTO
transaktion på nycklar i sysschobjs
släpps omedelbart. Undantaget är det första låset på (246708db8c1f)
.
Detta är meningsfullt eftersom planen visar kapslade loops-skanningar av [sys].[sysschobjs].[clst] [o]
och eftersom temporära objekt får negativa objekt-id kommer de att vara de första raderna som påträffas i skanningsordning.
Jag stötte också på situationen som beskrivs i OP där att köra en trevägs cross-join först verkar tillåta att den fyrvägs ett lyckas.
De första händelserna i spåret för SELECT INTO
transaktion det finns ett helt annat mönster.
Detta var efter en omstart av tjänsten så låsresursvärdena i textdatakolumnen är inte direkt jämförbara.
Istället för att behålla låset på den första nyckeln och sedan ett mönster för att skaffa och släppa efterföljande nycklar, verkar det få många fler lås utan att släppa dem först.
Jag antar att det måste finnas en viss variation i exekveringsstrategin som undviker problemet.
Uppdatera
Anslutningsobjektet jag tog upp om detta
har inte markerats som fixat men jag är nu på SQL Server 2012 SP2 och kan nu bara reproducera tillfällig självblockering snarare än permanent. Jag får fortfarande självblockering men efter ett antal misslyckade försök att köra droptemp
transaktionen lyckades verkar det gå tillbaka till att bearbeta användartransaktionen. Efter det genomförs systemtransaktionen framgångsrikt. Fortfarande på samma spinn. (åtta försök i ett exempelkörning. Jag är inte säker på om detta kommer att upprepas konsekvent)