Ik kan zou dit ook 100% van de tijd op mijn machine kunnen reproduceren. (zie opmerking aan het einde)
De kern van het probleem is dat je S
. uitschakelt vergrendelt op systeemtabelrijen in tempdb
die kunnen conflicteren met de sloten die nodig zijn voor interne tempdb
opruimtransacties.
Wanneer dit opruimwerk wordt toegewezen aan dezelfde sessie die eigenaar is van de S
lock een onbepaald vastlopen kan optreden.
Om dit probleem zeker te voorkomen, moet u stoppen met verwijzen naar het system
objecten binnen tempdb
.
Het is mogelijk om een getallentabel te maken zonder te verwijzen naar externe tabellen. Het volgende hoeft geen basistabelrijen te lezen en neemt dus ook geen sloten.
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
Stappen om te reproduceren
Maak eerst een procedure
CREATE PROC P
AS
SET NOCOUNT ON;
DECLARE @T TABLE (X INT)
GO
Start vervolgens de SQL-service opnieuw en voer in één verbinding uit
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
Vervolgens in een andere verbinding uitvoeren
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
De query die de Numbers-tabel vult, lijkt in een live lock-situatie te komen met de interne systeemtransacties die tijdelijke objecten opruimen, zoals tabelvariabelen.
Het is me gelukt om sessie-ID 53 op deze manier te blokkeren. Het is voor onbepaalde tijd geblokkeerd. De uitvoer van sp_WhoIsActive
laat zien dat deze spid bijna de hele tijd geschorst doorbrengt. In opeenvolgende runs worden de cijfers in de reads
kolom neemt toe, maar de waarden in de andere kolommen blijven grotendeels hetzelfde.
De wachttijd vertoont geen toenemend patroon, maar geeft wel aan dat de blokkering periodiek moet worden opgeheven voordat deze opnieuw wordt geblokkeerd.
SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id
Retourneren
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
| 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 |
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
Gebruik van de id in de beschrijving van de bron
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
Retourneren
+------------+
| name |
+------------+
| sysschobjs |
+------------+
Hardlopen
SELECT resource_description,request_status
FROM sys.dm_tran_locks
WHERE request_session_id = 53 AND request_status <> 'GRANT'
Retourneren
+----------------------+----------------+
| resource_description | request_status |
+----------------------+----------------+
| (246708db8c1f) | CONVERT |
+----------------------+----------------+
Verbinden via de DAC en draaien
SELECT id,name
FROM tempdb.sys.sysschobjs WITH (NOLOCK)
WHERE %%LOCKRES%% = '(246708db8c1f)'
Retourneren
+-------------+-----------+
| id | name |
+-------------+-----------+
| -1578606288 | #A1E86130 |
+-------------+-----------+
Benieuwd wat dat is
SELECT name,user_type_id
FROM tempdb.sys.columns
WHERE object_id = -1578606288
Retourneren
+------+--------------+
| name | user_type_id |
+------+--------------+
| X | 56 |
+------+--------------+
Dit is de kolomnaam in de tabelvariabele die wordt gebruikt door het opgeslagen proces.
Hardlopen
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)'
Retourneren
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| 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 |
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
Dus de SELECT INTO
transactie houdt een S
vast lock op de rij in tempdb.sys.sysschobjs
met betrekking tot de tabelvariabele #A1E86130
. De droptemp
transactie krijgt geen X
vergrendel deze rij vanwege deze conflicterende S
slot.
Als u deze query herhaaldelijk uitvoert, blijkt dat de transaction_id
voor de droptemp
transactie verandert herhaaldelijk.
Ik speculeer dat SQL Server deze interne transacties op gebruikers-spids moet toewijzen en prioriteit moet geven voordat het gebruikerswerk wordt gedaan. Dus de sessie-ID 53 zit vast in een constante cyclus waarin het een droptemp
. opstart transactie, wordt geblokkeerd door de gebruikerstransactie die op dezelfde spid draait. Draait de interne transactie terug en herhaalt het proces voor onbepaalde tijd.
Dit wordt bevestigd door de verschillende vergrendelings- en transactiegebeurtenissen in SQL Server Profiler te traceren nadat de spid is vastgelopen.
Ik heb ook de vergrendelingsgebeurtenissen daarvoor getraceerd.
Gebeurtenissen blokkeren
De meeste gedeelde sleutelvergrendelingen verwijderd door de SELECT INTO
transactie op sleutels in sysschobjs
onmiddellijk vrijkomen. De uitzondering is de eerste vergrendeling op (246708db8c1f)
.
Dit is logisch, aangezien het plan scans van geneste lussen toont van [sys].[sysschobjs].[clst] [o]
en omdat tijdelijke objecten negatieve object-ID's krijgen, zullen ze de eerste rijen zijn die in scanvolgorde worden aangetroffen.
Ik kwam ook de situatie tegen die in de OP wordt beschreven, waarbij het uitvoeren van een drieweg-kruisverbinding eerst de vierweg lijkt te laten slagen.
De eerste paar gebeurtenissen in de trace voor de SELECT INTO
transactie zijn er een heel ander patroon.
Dit was na een herstart van de service, dus de vergrendelingsbronwaarden in de tekstgegevenskolom zijn niet direct vergelijkbaar.
In plaats van het slot op de eerste sleutel vast te houden en vervolgens een patroon van het verkrijgen en vrijgeven van volgende sleutels, lijkt het veel meer sloten te verwerven zonder ze in eerste instantie los te laten.
Ik neem aan dat er een variantie moet zijn in de uitvoeringsstrategie die het probleem vermijdt.
Bijwerken
Het Connect-item dat ik heb opgehaald hierover
is niet gemarkeerd als vast, maar ik gebruik nu SQL Server 2012 SP2 en kan nu alleen tijdelijke zelfblokkering reproduceren in plaats van permanent. Ik krijg nog steeds de zelfblokkering, maar na een aantal mislukte pogingen om de droptemp
. uit te voeren transactie succesvol blijkt terug te gaan naar het verwerken van de gebruikerstransactie. Nadat de systeemtransactie is vastgelegd, wordt deze met succes uitgevoerd. Nog steeds op dezelfde spi. (acht pogingen in één voorbeeldrun. Ik weet niet zeker of dit consequent zal worden herhaald)