Io posso potrebbe anche riprodurre questo 100% delle volte sulla mia macchina. (vedi nota in fondo)
Il succo del problema è che stai eliminando S
blocca le righe della tabella di sistema in tempdb
che possono entrare in conflitto con i blocchi necessari per tempdb
interni transazioni di pulizia.
Quando questo lavoro di pulizia viene assegnato alla stessa sessione che possiede la S
lock può verificarsi un blocco indefinito.
Per evitare questo problema, devi smettere di fare riferimento al system
oggetti all'interno di tempdb
.
È possibile creare una tabella numeri senza fare riferimento a tabelle esterne. Quanto segue non ha bisogno di leggere righe della tabella di base e quindi non accetta blocchi.
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
Passaggi per la riproduzione
Per prima cosa crea una procedura
CREATE PROC P
AS
SET NOCOUNT ON;
DECLARE @T TABLE (X INT)
GO
Quindi riavvia il servizio SQL e in una connessione esegui
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
Quindi in un'altra connessione esegui
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
La query che popola la tabella di Numbers sembra riuscire a entrare in una situazione di blocco in tempo reale con le transazioni interne del sistema che ripuliscono oggetti temporanei come le variabili di tabella.
Sono riuscito a bloccare l'ID sessione 53 in questo modo. È bloccato a tempo indeterminato. L'output di sp_WhoIsActive
mostra che questo spid trascorre quasi tutto il tempo sospeso. In esecuzioni consecutive i numeri nelle reads
colonna aumenta ma i valori nelle altre colonne rimangono sostanzialmente gli stessi.
La durata dell'attesa non mostra uno schema crescente, sebbene indichi che deve essere sbloccata periodicamente prima di essere nuovamente bloccata.
SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id
Resi
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
| 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 |
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
Utilizzando l'id nella descrizione della risorsa
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
Resi
+------------+
| name |
+------------+
| sysschobjs |
+------------+
In esecuzione
SELECT resource_description,request_status
FROM sys.dm_tran_locks
WHERE request_session_id = 53 AND request_status <> 'GRANT'
Resi
+----------------------+----------------+
| resource_description | request_status |
+----------------------+----------------+
| (246708db8c1f) | CONVERT |
+----------------------+----------------+
Connessione tramite DAC e funzionamento
SELECT id,name
FROM tempdb.sys.sysschobjs WITH (NOLOCK)
WHERE %%LOCKRES%% = '(246708db8c1f)'
Resi
+-------------+-----------+
| id | name |
+-------------+-----------+
| -1578606288 | #A1E86130 |
+-------------+-----------+
Curioso di sapere di cosa si tratta
SELECT name,user_type_id
FROM tempdb.sys.columns
WHERE object_id = -1578606288
Resi
+------+--------------+
| name | user_type_id |
+------+--------------+
| X | 56 |
+------+--------------+
Questo è il nome della colonna nella variabile di tabella utilizzata dal processo memorizzato
In esecuzione
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)'
Resi
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| 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 |
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
Quindi il SELECT INTO
transazione contiene una S
blocca la riga in tempdb.sys.sysschobjs
relativo alla variabile di tabella #A1E86130
. Il droptemp
la transazione non può ottenere una X
blocca questa riga a causa di questo S
in conflitto serratura.
L'esecuzione ripetuta di questa query rivela che il transaction_id
per il droptemp
transazione cambia ripetutamente.
Suppongo che SQL Server debba allocare queste transazioni interne sugli spids degli utenti e dare loro la priorità prima di eseguire il lavoro dell'utente. Quindi l'id di sessione 53 è bloccato in un ciclo costante in cui avvia un droptemp
transazione, è bloccato dalla transazione utente in esecuzione sullo stesso spid. Esegue il rollback della transazione interna, quindi ripete il processo all'infinito.
Ciò è confermato dalla traccia dei vari eventi di blocco e transazione in SQL Server Profiler dopo che lo spid si è bloccato.
Ho anche tracciato gli eventi di blocco prima di quello.
Blocca il blocco degli eventi
La maggior parte dei blocchi a chiave condivisa eliminati dal SELECT INTO
transazione sulle chiavi in sysschobjs
essere rilasciato immediatamente. L'eccezione è il primo blocco su (246708db8c1f)
.
Questo ha un senso in quanto il piano mostra le scansioni dei loop nidificati di [sys].[sysschobjs].[clst] [o]
e poiché agli oggetti temporanei vengono assegnati ID oggetti negativi, saranno le prime righe incontrate nell'ordine di scansione.
Ho anche riscontrato la situazione descritta nell'OP in cui l'esecuzione di un cross join a tre vie sembra consentire il successo di quella a quattro vie.
I primi eventi nella traccia per SELECT INTO
transazione ci sono uno schema completamente diverso.
Ciò è avvenuto dopo il riavvio del servizio, quindi i valori delle risorse di blocco nella colonna dei dati di testo non sono direttamente confrontabili.
Invece di mantenere il blocco sulla prima chiave e quindi uno schema di acquisizione e rilascio delle chiavi successive, sembra acquisire molti più blocchi senza rilasciarli inizialmente.
Presumo che debba esserci qualche variazione nella strategia di esecuzione che eviti il problema.
Aggiorna
L'elemento Connect che ho sollevato su questo
non è stato contrassegnato come corretto, ma ora sono su SQL Server 2012 SP2 e ora posso riprodurre solo il blocco automatico temporaneo anziché permanente. Ottengo ancora il blocco automatico ma dopo un certo numero di tentativi falliti di eseguire droptemp
transazione riuscita sembra tornare all'elaborazione della transazione utente. Dopo aver eseguito il commit, la transazione di sistema viene eseguita correttamente. Sempre allo stesso ritmo. (otto tentativi eseguiti in un esempio. Non sono sicuro che questo verrà ripetuto in modo coerente)