2010-01-20 3 views
10

Qualcuno può aiutarmi per un deadlock in SQL Server 2005?deadlock di SQL Server 2005 con indice non cluster

Per un semplice test, ho una tabella "Libro" che ha una chiave primaria (id) e un nome di colonna. L'indice predefinito di questa chiave primaria è non cluster.

Il deadlock si verifica quando due sessioni vengono eseguite contemporaneamente. Il monitor delle attività mostra la prima sessione "// passaggio 1" che blocca la riga (blocco della rimozione) con il blocco X. La seconda sessione mantiene il blocco della riga U e il blocco della chiave U. L'immagine deadlock mostra "// step2" della prima sessione richiede il blocco U del tasto.

Se l'indice è in cluster, in questo caso non esiste un deadlock. "// step 1" manterrà la riga e il blocco dei tasti contemporaneamente, quindi non ci sono problemi. Posso capire che bloccare una riga bloccherà anche l'indice poiché il nodo foglia dell'indice cluster è costituito da dati di riga.

Ma, perché l'indice non cluster è in questo modo? Se la seconda sessione contiene il blocco U della chiave, perché il "passaggio 1" della prima sessione non contiene questo blocco poiché è lo stesso dell'istruzione di aggiornamento.

--// first session 
BEGIN TRAN 
    update Book set name = name where id = 1 //step 1 
    WaitFor Delay '00:00:20' 
    update Book set name = 'trans' where id = 1 //step2 
COMMIT 

--// second session 
BEGIN TRAN 
--// this statement will keep both RID(U lock) and KEY(U lock) if first session did not use HOLDLOCK 
    update Book set name = name where id = 1 
COMMIT 
+0

avete una chiave in cluster sulla vostra tabella di esempio del libro ?? Non penso che sia una questione se il tuo PK è in cluster o no - penso che sia più importante sapere se hai effettivamente una Clustered Key (e quindi una tabella cluster), o se hai a che fare con un heap (nessun indice cluster) –

+0

Puoi aggiungere l'output rilevante di 'exec sp_lock' mentre è in deadlock? – Andomar

+0

Da sqlprofiler, l'immagine deadlock mostra "// step2" della prima sessione richiede il blocco della chiave U, ma è tenuto da seesion2. –

risposta

10

Il fattore rilevante qui è che si sta utilizzando una colonna nella clausola where con un indice non cluster. Quando SQL Server elabora un aggiornamento, è più o meno così:

  1. Trova righe da aggiornare, prendendo U serrature su dati toccati
  2. aggiornare le righe, prendendo serrature X sui dati modificati

Dopo la l'istruzione completa (con l'isolamento predefinito READ COMMITTED), i blocchi U vengono rilasciati, ma i blocchi X vengono mantenuti fino alla fine della transazione per mantenere l'isolamento.

Nella situazione dell'indice non cluster, SQL Server cerca l'indice su id e lo utilizza per cercare la riga effettiva.Il bloccaggio gioca come questo:

  1. (Session 1, punto 1) blocco U scattata sull'indice valore chiave per id = 1
  2. (Session 1, punto 1) X serratura taken on RID per riga con id = 1
  3. (Session 1, punto 1) blocco U rilasciato
  4. (sessione 2) blocco U scattata sull'indice valore chiave per id = 1
  5. (sessione 2) blocco X bloccata per RID per riga con id = 1
  6. (Sessione 1, passaggio 2) Blocco U bloccato sul valore della chiave dell'indice per id = 1 - DEADLOCK

Tuttavia, quando l'indice è l'indice cluster, non v'è un passaggio separato per convertire la chiave dell'indice nella riga - il valore indice cluster è l'identificatore di riga. Pertanto, il bloccaggio finisce così:

  1. (Session 1, punto 1) blocco U scattata sull'indice valore chiave per id = 1
  2. (Session 1, punto 1) blocco U aggiornato a X serratura
  3. (sessione 2) blocco U bloccato sull'indice valore chiave per id = 1
  4. (Session 1, passaggio 2) Serratura già tenuta indice valore chiave per id = 1
  5. (Session 1, commit) serratura rilasciato
  6. (Sessione 2) Blocco U concesso
  7. (Sessione 2) blocco U aggiornato a blocco X
  8. (Sessione 2) Serratura rilasciato

Come sempre, tenere presente che mentre questo può essere il piano di query utilizzato in questo caso, l'ottimizzatore è libero di fare le cose in modo diverso. Ad esempio, può scegliere una scansione della tabella o estrarre più lucchetti a grana grossa. In questi casi il deadlock potrebbe non accadere.

+0

Grazie mille, ha senso per me. Il problema sembra essere "passaggio separato per la conversione della chiave dell'indice nella riga" nell'indice non cluster. –

+0

Se aggiungo il suggerimento di holdlock nell'istruzione di aggiornamento per entrambe le sessioni, questo viene risolto poiché U e X lock sono ancora trattenuti nella sessione 1. La mia comprensione è holdlock richiede solo S lock, mi chiedo perché holdlock potrebbe consentire alla sessione 1 di mantenere Blocco U per chiave e blocco X per RID? Grazie. –

+0

Potrebbe holdlock causare altri potenziali problemi (vale a dire un altro deadlock, downgrade delle prestazioni)? –

0

Questo link ha un sacco di suggerimenti utili: SQL Server deadlocks between select/update or multiple selects.

Qui ci sono alcuni punti da considerare che potrebbe aiutare le persone per rispondere alla tua domanda:

  1. Cosa transazione livello di isolamento stai usando?
  2. È consentita l'escalation del blocco (ad esempio da una riga all'altra)?
  3. C'è un indice sulla colonna 'nome'?
+0

1.read comitted 2.use impostazione predefinita da SQLServer 3. Nessun altro indice. –

0

Il tuo primo aggiornamento in realtà non modifica nulla:

update Book set name = name where id = 1 

Thy un comando che cambia in realtà la colonna, poi un blocco esclusivo si terrà sulla riga.