Jeg kan kunne også gengive dette 100% af tiden på min maskine. (se note til sidst)
Kernen i problemet er, at du tager S
ud låser på systemtabelrækker i tempdb
der kan være i konflikt med de låse, der er nødvendige for intern tempdb
oprydningstransaktioner.
Når dette oprydningsarbejde allokeres til den samme session, som ejer S
lås et ubestemt hæng kan forekomme.
For at undgå dette problem med sikkerhed skal du stoppe med at referere til systemet
objekter inde i tempdb
.
Det er muligt at oprette en taltabel uden at referere til eksterne tabeller overhovedet. Følgende skal ikke læse nogen basistabelrækker og tager derfor heller ingen låse.
Med Ti(N) AS (VÆLG 1 UNION ALLE VÆLG 1 UNION ALLE VÆLG 1 UNION ALLE VÆLG 1 UNION ALLE VÆLG 1 UNION ALLE VÆLG 1 UNION ALLE VÆLG 1 UNION ALLE VÆLG 1 UNION ALLE VÆLG 1 UNION ALLE VÆLG 1) SELECT TOP 1000000 IDENTITY(INT, 1, 1) NumberINTO NumbersFROM Ti T10, Ti T100, Ti T1000, Ti T10000, Ti T100000, Ti T1000000
Trin til gengivelse
Opret først en procedure
OPRET PROC PAS SET NOCOUNT ON; DECLARE @T TABLE (X INT)GO
Genstart derefter SQL Service og udfør i én forbindelse
MENS IKKE FINDER(SELECT * FROM sys.dm_os_waiting_tasks WHERE session_id =blocking_session_id) BEGIN /*Dette vil forårsage de problematiske droptemp-transaktioner*/ EXEC sp_rekompilere 'P' EXEC P END;SELECT *FROM sys.dm_taskWit blocking_session_id
Kør derefter i en anden forbindelse
BRUG tempdb;SELECT TOP 1000000 IDENTITY(INT, 1, 1) NumberINTO #TFROM sys.objects s1CROSS JOIN sys.objects s2CROSS JOIN sys.objects s3CROSS JOIN sys.objects TABLEDROP #TABLEDROP #TABLEDROP s4;>
Forespørgslen, der udfylder Numbers-tabellen, ser ud til at lykkes med at komme ind i en live-lås-situation med de interne systemtransaktioner, der rydder op i midlertidige objekter såsom tabelvariabler.
Det lykkedes mig at få blokeret session-id 53 på denne måde. Den er blokeret på ubestemt tid. Outputtet af sp_WhoIsActive
viser, at denne edderkop tilbringer næsten hele tiden suspenderet. I fortløbende kørsler tallene i reads
kolonne stiger, men værdierne i de andre kolonner forbliver stort set de samme.
Ventetiden viser ikke et stigende mønster, men det indikerer, at blokeringen skal ophæves med jævne mellemrum, før den blokeres igen.
VÆLG *FRA sys.dm_os_waiting_tasksWHERE session_id =blocking_session_id
Returnerer
+----------------------+-----------+------- ----------+------------------+-----------+-------- --------------+-------------------------+------------- ---------------------------------------+-------------- -------------------------------------------------- ----------------------------------+| venter_opgave_adresse | session_id | exec_context_id | wait_duration_ms | vente_type | ressource_adresse | blokeringsopgaveadresse | 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 associeretObjectId=281474978938880 |+-----------------+--------- ---+----------------+------------------------+--------- --+--------------------+----------------------+-- ------------------+---------------------------+--- -------------------------------------------------- ----------------------------------------------------+
Brug af id'et i ressourcebeskrivelsen
SELECT o.nameFROM sys.allocation_units au MED (NOLOCK) INNER JOIN sys.partitions p MED (NOLOCK) ON au.container_id =p.partition_id INNER JOIN sys.all_objects o MED (NOLOCK) ON o.object_id =p.object_idWHERE allocation_unit_id =281474978938880
Returnerer
+-------------+| navn |+------------+| sysschobjs |+------------+
Kører
SELECT resource_description,request_statusFROM sys.dm_tran_locks WHERE request_session_id =53 OG request_status <> 'GANT'
Returnerer
+-----------------------------+----------------+| ressourcebeskrivelse | request_status |+----------------------+----------------+| (246708db8c1f) | KONVERTER |+-----------------------------+----------------+
Tilslutter via DAC'en og kører
VÆLG id,navnFRA tempdb.sys.sysschobjs MED (NOLOCK)WHERE %%LOCKRES%% ='(246708db8c1f)'
Returnerer
+-------------+-----------+| id | navn |+-------------+-----------+| -1578606288 | #A1E86130 |+-------------+-----------+
Nysgerrig efter hvad det er
SELECT name,user_type_idFROM tempdb.sys.columnsWHERE object_id =-1578606288
Returnerer
+------+--------------+| navn | user_type_id |+------+----------------+| X | 56 |+------+--------------+
Dette er kolonnenavnet i tabelvariablen, der bruges af den lagrede proc.
Kører
SELECT request_mode, request_status, request_session_id, request_owner_id, lock_owner_address, t.transaction_id, t.name, t.transaction_begin_timeFROM sys.dm_tran_locks l JOIN sys.dm_tran_active_transactionsrequest =transaction_ownert_ON l.cHEREd_ON l.bHEREt_ON l.bHEREt_ON l. )'
Returnerer
+--------------+----------------+----------- ----------+-------------------+--------------------+ --------------+-------------+--------------------- ------+| request_mode | request_status | request_session_id | request_owner_id | låseejeradresse | transaktions-id | navn | transaktions_begyndelsestidspunkt |+--------------+----------------+-------------- -----+------------------+--------------------+---- --------------+-------------+----------------------- --+| U | TILSKUD | 53 | 227647 | 0x00000002F1EF6800 | 227647 | droptemp | 24-11-2013 18:36:28.267 || S | TILSKUD | 53 | 191790 | 0x00000002F9B16380 | 191790 | VÆLG TIL | 24-11-2013 18:21:30.083 || X | KONVERTER | 53 | 227647 | 0x00000002F9B12FC0 | 227647 | droptemp | 2013-11-24 18:36:28.267 |+-------------------+----------------+------ --------------+------------------------+---------------- ----+----------------+--------------+-------- -----------+
Så SELECT INTO
transaktionen indeholder en S
lås på rækken i tempdb.sys.sysschobjs
vedrørende tabelvariablen #A1E86130
. droptemp
transaktionen kan ikke få et X
lås på denne række på grund af denne modstridende S
lås.
At køre denne forespørgsel gentagne gange afslører, at transaction_id
for droptemp
transaktionen ændres gentagne gange.
Jeg spekulerer i, at SQL Server skal allokere disse interne transaktioner på brugerspidser og prioritere dem, før de udfører brugerarbejdet. Så sessions-id 53 sidder fast i en konstant cyklus, hvor den starter en droptemp
transaktion, blokeres af brugertransaktionen, der kører på samme spid. Ruller den interne transaktion tilbage og gentager derefter processen på ubestemt tid.
Dette bekræftes ved at spore de forskellige låse- og transaktionsbegivenheder i SQL Server Profiler, efter at spiden er blevet hængt.
Jeg sporede også låsebegivenhederne forud for det.
Lås hændelsesblokering
De fleste af de delte nøglelåse fjernes af SELECT INTO
transaktion på nøgler i sysschobjs
blive løsladt med det samme. Undtagelsen er den første lås på (246708db8c1f)
.
Dette giver mening, da planen viser indlejrede loops-scanninger af [sys].[sysschobjs].[clst] [o]
og fordi midlertidige objekter får negative objektider, vil de være de første rækker, der stødes på i scanningsrækkefølge.
Jeg stødte også på den situation, der er beskrevet i OP, hvor at køre en trevejs krydsforbindelse først ser ud til at tillade, at den firevejs ene kan lykkes.
De første par hændelser i sporet for SELECT INTO
transaktion er der et helt andet mønster.
Dette var efter en genstart af tjenesten, så låseressourceværdierne i tekstdatakolonnen er ikke direkte sammenlignelige.
I stedet for at fastholde låsen på den første nøgle og derefter et mønster med at erhverve og frigive efterfølgende nøgler, ser det ud til at erhverve mange flere låse uden at frigive dem i starten.
Jeg formoder, at der må være en vis variation i udførelsesstrategien, der undgår problemet.
Opdater
Det Connect-element, jeg rejste om dette
er ikke blevet markeret som rettet, men jeg er nu på SQL Server 2012 SP2 og kan nu kun reproducere midlertidig selvblokering i stedet for permanent. Jeg får stadig selvblokering, men efter nogle mislykkede forsøg på at udføre droptemp
transaktionen med succes ser ud til at gå tilbage til behandling af brugertransaktionen. Derefter udføres systemtransaktionen med succes. Stadig på samme spids. (otte forsøg i et eksempel. Jeg er ikke sikker på, om dette vil blive gentaget konsekvent)