2010-04-29 19 views
7

Ho un problema con più deadlock sul server SQL 2005. Questo è tra un'istruzione INSERT e SELECT.deadlock del server SQL tra l'istruzione INSERT e SELECT

Ci sono due tabelle. Tabella 1 e Tabella 2. Table2 ha il PK di Table1 (table1_id) come chiave esterna.
L'indice su table1_id è in cluster.

L'INSERISCE inserisce una singola riga in tabella2 alla volta.
SELCET unisce le 2 tabelle. (è una lunga query che potrebbe richiedere fino a 12 secondi)

In base alle mie conoscenze (ed esperimenti), l'INSERIMENTO dovrebbe acquisire un blocco IS sulla tabella1 per verificare l'integrità referenziale (che non dovrebbe causare un deadlock). Ma, in questo caso, ha acquistato una pagina IX bloccare

Il rapporto stallo:

<deadlock-list> 
<deadlock victim="process968898"> 
    <process-list> 
    <process id="process8db1f8" taskpriority="0" logused="2424" waitresource="OBJECT: 5:789577851:0 " waittime="12390" ownerId="61831512" transactionname="user_transaction" lasttranstarted="2010-04-16T07:10:13.347" XDES="0x222a8250" lockMode="IX" schedulerid="1" kpid="3764" status="suspended" spid="52" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2010-04-16T07:10:13.350" lastbatchcompleted="2010-04-16T07:10:13.347" clientapp=".Net SqlClient Data Provider" hostname="VIDEV01-B-ME" hostpid="3040" loginname="DatabaseName" isolationlevel="read uncommitted (1)" xactid="61831512" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056"> 
    <executionStack> 
    <frame procname="DatabaseName.dbo.prcTable2_Insert" line="18" stmtstart="576" stmtend="1148" sqlhandle="0x0300050079e62d06e9307f000b9d00000100000000000000"> 
INSERT INTO dbo.Table2 
    (
     f1, 
     table1_id, 
     f2 
    ) 
    VALUES 
    (
     @p1, 
     @p_DocumentVersionID, 
     @p1 

    )  </frame> 
    </executionStack> 
    <inputbuf> 
Proc [Database Id = 5 Object Id = 103671417] </inputbuf> 
    </process> 
    <process id="process968898" taskpriority="0" logused="0" waitresource="PAGE: 5:1:46510" waittime="7625" ownerId="61831406" transactionname="INSERT" lasttranstarted="2010-04-16T07:10:12.717" XDES="0x418ec00" lockMode="S" schedulerid="2" kpid="1724" status="suspended" spid="53" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2010-04-16T07:10:12.713" lastbatchcompleted="2010-04-16T07:10:12.713" clientapp=".Net SqlClient Data Provider" hostname="VIDEV01-B-ME" hostpid="3040" loginname="DatabaseName" isolationlevel="read committed (2)" xactid="61831406" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056"> 
    <executionStack> 
    <frame procname="DatabaseName.dbo.prcGetList" line="64" stmtstart="3548" stmtend="11570" sqlhandle="0x03000500dbcec17e8d267f000b9d00000100000000000000"> 
     <!-- XXXXXXXXXXXXXX...SELECT STATEMENT WITH Multiple joins including both Table2 table 1 and .... XXXXXXXXXXXXXXX --> 
    </frame> 
    </executionStack> 
    <inputbuf> 
Proc [Database Id = 5 Object Id = 2126630619] </inputbuf> 
    </process> 
    </process-list> 
    <resource-list> 
    <pagelock fileid="1" pageid="46510" dbid="5" objectname="DatabaseName.dbo.table1" id="lock6236bc0" mode="IX" associatedObjectId="72057594042908672"> 
    <owner-list> 
    <owner id="process8db1f8" mode="IX"/> 
    </owner-list> 
    <waiter-list> 
    <waiter id="process968898" mode="S" requestType="wait"/> 
    </waiter-list> 
    </pagelock> 
    <objectlock lockPartition="0" objid="789577851" subresource="FULL" dbid="5" objectname="DatabaseName.dbo.Table2" id="lock970a240" mode="S" associatedObjectId="789577851"> 
    <owner-list> 
    <owner id="process968898" mode="S"/> 
    </owner-list> 
    <waiter-list> 
    <waiter id="process8db1f8" mode="IX" requestType="wait"/> 
    </waiter-list> 
    </objectlock> 
    </resource-list> 
</deadlock> 
</deadlock-list> 

qualcuno può spiegare il motivo per cui l'INSERT ottiene il blocco di pagina IX?
Non sto leggendo il rapporto deadlock correttamente?
BTW, non sono riuscito a riprodurre questo problema.

Grazie!

EDIT: TABELLE CREAZIONE:

CREATE TABLE [dbo].[Table2] (
    [Table2_id] [int] IDENTITY (1, 1) NOT NULL , 
    [f1] [int] NULL , 
    [Table1_id] [int] NOT NULL , 
    [f2] [int] NOT NULL , 
) 

ALTER TABLE [dbo].[Table2] ADD 
    CONSTRAINT [FK_Table2_Table1] FOREIGN KEY 
    (
     [Table1_id] 
    ) REFERENCES [dbo].[Table1] (
     [Table1_id] 
    ) 


CREATE TABLE [dbo].[Table1] (
    [Table1_id] [int] IDENTITY (1, 1) NOT NULL , 
) 

ALTER TABLE [dbo].[Table1] WITH NOCHECK ADD 
    CONSTRAINT [PK_Table1] PRIMARY KEY CLUSTERED 
    (
     [Table1_id] 
    ) 
+0

È possibile aggiungere le istruzioni DDL per le tabelle e la chiave esterna? –

risposta

2

In un punt, direi che DatabaseName.dbo.prcTable2_Insert sta eseguendo all'interno di una transazione o apre in modo esplicito uno, ed è (o la connessione con l'aperta transazione) ha già fatto un inserimento in Tabella1 in anticipo.

+0

Penso che tu mi abbia preso sulla strada giusta qui. Ho trovato una transazione con un aggiornamento a TABLE1 appena DOPO la chiamata all'inserto. L'ordine di blocco non ha comunque senso per me. Perché ottiene un lucchetto IX prima che ne abbia bisogno? non dovrebbe aumentare il blocco dopo che l'inserimento è stato eseguito? – dtroy

+0

BTW, non riesco ancora a riprodurlo da solo. Ho creato una transazione con questi due inserimenti/aggiornamenti, ho eseguito in un ciclo in parallelo per l'esecuzione di un ciclo con quello SELECT. Qualche idea ? – dtroy

+0

@dtroy - Temo di non avere molto altro da suggerire, di non riuscire a riprodurlo (e, naturalmente, se riesci a riprodurlo, dubito che tu abbia bisogno di ulteriore aiuto qui in ogni caso). –

6

I significa blocco 'intent' e sono sempre associati a gerarchie. Poiché il gestore blocchi non comprende la struttura fisica, è impossibile per lui onorare i blocchi gerarchici, quindi la gerarchia viene ricreata in blocchi di intenti.

Nel proprio caso l'INSERT ha un blocco di intento su una pagina. Ciò implica che ha anche ottenuto un blocco X su una riga nella pagina, che è il comportamento normale. Ora cerca di ottenere un nuovo blocco IX, quindi probabilmente ha bisogno di inserire una riga in una pagina diversa. Questo sarebbe normale comportamento di un inserto in una tabella con più indici: il primo IX è su uno degli indici (possibile il cluster) e il secondo IX è su un indice non cluster.

La SELECT che dici restituisce in 12 secondi, quindi è una lunga query, su un set di dati di grandi dimensioni, e il piano probabilmente ha scelto una granularità di blocco alta, blocchi di pagina. SELECT ha un blocco S sulla pagina che INSERT vuole il blocco IX e desidera un altro blocco S sulla pagina che INSERT abbia il blocco IX.

Questo è un deadlock banale e dovrebbe essere molto facile risolverlo: assicurarsi che il SELEZIONA non abbia bisogno di quei blocchi a S di pagina. Questo non è un difetto INSERT qui. Non sapendo cosa fa il SELECt non posso dire con certezza se sia ottimale o meno. Nella mia esperienza, quasi sempre un SELECT come questo ha un sacco, molto e molto spazio per migliorare (eitehr lo stesso SELECT o lo schema sottostante).

ma accettando che il SELECT è ottimale, il tuo più semplice carta get-out-of-carcere è quello di trasformare il row versioning:

ALTER DATABASE <dbname> SET ALLOW_SNAPSHOT_ISOLATION ON; 
ALTER DATABASE <dbname> SET READ_COMMITTED_SNAPSHOT ON; 

Aggiornamento:

realtà in seconda lettura è evidente che l'inserto ha blocchi su tabelle diverse (a meno che non abbiate modificato l'XML, che sembra modificato manualmente qui e là), quindi la vostra spiegazione su come l'inserto agisce deve essere errata. L'INSERT fa parte di una transazione rispetto a almeno due scritture, una su Table1 e una su Table2. Ma questo non cambia molto il problema né la soluzione. È vero che hai la possibilità di suddividere le due scritture nella transazione in transazioni separate, ma quella ovviamente è la strada peggiore.

+0

Dovrebbe esserci una sola scrittura. Ho modificato l'xml per semplificare le cose e rimuovere alcuni nomi sensibili. – dtroy