2009-05-08 8 views
5

Ho un'applicazione java che sta eseguendo più operazioni CRUD simultanee su un database. Sto aggiungendo il supporto per SQLServer ma sto avendo problemi con deadlock durante le eliminazioni simultanee. Dopo alcune indagini è emerso che il problema potrebbe essere dovuto al blocco dell'escalation su un determinato tavolo.deadlock SQLServer

Nel tentativo di risolvere il problema, ho deciso di eseguire tutte le letture sul tavolo in questione "per aggiornamento" utilizzando l'hint UPDLOCK in modo da evitare il deadlock. Tuttavia, sto ancora vedendo il problema. Ho permesso tracing in SQLServer e hanno trovato la seguente traccia di stallo nei registri di SQLServer:

Deadlock incontrato .... Stampa di informazioni deadlock grafo delle attese

nodo: 1 CHIAVE: 5: 72057594042384384 (54048e7b3828) CleanCnt: 3 Modalità: X Flags: 0x0 Concessione Lista 1: proprietario: 0x03D08C40 Modalità: X Flg: 0x0 Ref: 0 di vita: 02 milioni SPID: 62 ECID: 0 XactLockInfo: 0x04834274 SPID: 62 ECID: 0 Statement Digitare: DELETE Line #: 1 Input Buf: Language Event: (@ P0 nvarchar (4000)) delete from part_data where part_id = @ P0 Richiesto da: ResType: LockOwner Stype: 'OR'Xdes: 0x04B511C8 Modalità: U SPID: 60 BatchID: 0 ECID: 0 TaskProxy: (0x058BE378) Valore: 0x3d08500 Costo: (0/1296)

Nodo: 2

LEGENDA: 5: 72057594042384384 (f903d6d6e0ac) CleanCnt: 2 Modalità: X Flags: 0x0 Concessione Lista 0: proprietario: 0x03D088A0 Modalità: X Flg: 0x0 Ref: 0 di vita: 02 milioni SPID: 60 ECID: 0 XactLockInfo: 0x04B511EC SPID: 60 ECID: 0 Tipo di istruzione: DELETE Riga numero: 1 Input Buf: Language Event: (@ P0 nvarchar (4000)) delete from part_data where part_id = @ P0 ResType: LockOwner Stype: 'OR'Xdes: 0x04834250 Modalità: U SPID: 62 BatchID: 0 ECID: 0 TaskProxy :(0x047BA378) Valore: Costo 0x3d089e0: (0/4588)

Vittima Resource Proprietario: restype: LockOwner Stype: 'OR'Xdes: Modalità 0x04B511C8: U SPID: 60 BatchID: 0 ECID: 0 TaskProxy: (0x058BE378) Valore : 0x3d08500 Costo: (0/1296)

SQLServer profiler lo mostra come due client con blocchi di aggiornamento (U) e che tentano di eseguire l'escalation su blocchi (X) esclusivi. I documenti SQLServer che ho letto dicono che solo un client può avere un blocco (U) su una tabella in un dato momento, quindi mi chiedo perché sto visualizzando la situazione mostrata nella traccia.

L'oggetto di database a cui fa riferimento quella traccia è un indice su una chiave esterna. Se qualcuno con esperienza nel risolvere questo tipo di problemi potrebbe offrire consigli, sarebbe di grande aiuto.

Grazie, Brad.

EDIT aggiunto stallo grafico XML come richiesto:

<deadlock-list> 
<deadlock victim="process989018"> 
    <process-list> 
    <process id="process6aa7a8" taskpriority="0" logused="4844" waitresource="KEY: 5:72057594042384384 (5504bdfb7529)" waittime="9859" ownerId="613553" transactionname="implicit_transaction" lasttranstarted="2009-05-08T11:52:39.137" XDES="0x5fcbc30" lockMode="U" schedulerid="1" kpid="3516" status="suspended" spid="59" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2009-05-08T11:52:39.183" lastbatchcompleted="2009-05-08T11:52:39.183" clientapp="jTDS" hostname="LOIRE" hostpid="123" loginname="sa" isolationlevel="read committed (2)" xactid="613553" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058"> 
    <executionStack> 
    <frame procname="adhoc" line="1" stmtstart="40" sqlhandle="0x0200000007c76c39efdd8317c6fa7b611b4fd958f05cfcf4"> 
delete from part_data where part_id = @P0  </frame> 
    </executionStack> 
    <inputbuf>(@P0 nvarchar(4000))delete from part_data where part_id = @P0</inputbuf> 
    </process> 
    <process id="process989018" taskpriority="0" logused="1528" waitresource="KEY: 5:72057594042384384 (5e0405cb0377)" waittime="1250" ownerId="613558" transactionname="implicit_transaction" lasttranstarted="2009-05-08T11:52:39.183" XDES="0x48318f0" lockMode="U" schedulerid="2" kpid="2692" status="suspended" spid="60" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2009-05-08T11:52:39.183" lastbatchcompleted="2009-05-08T11:52:39.183" clientapp="jTDS" hostname="LOIRE" hostpid="123" loginname="sa" isolationlevel="read committed (2)" xactid="613558" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058"> 
    <executionStack> 
    <frame procname="adhoc" line="1" stmtstart="40" sqlhandle="0x0200000007c76c39efdd8317c6fa7b611b4fd958f05cfcf4"> 
delete from part_data where part_id = @P0  </frame> 
    </executionStack> 
    <inputbuf>(@P0 nvarchar(4000))delete from part_data where part_id = @P0</inputbuf> 
    </process> 
    </process-list> 
    <resource-list> 
    <keylock hobtid="72057594042384384" dbid="5" objectname="MESSAGESTOREDB61.dbo.part_data" indexname="idx_part_data_part_id" id="lock3cab740" mode="X" associatedObjectId="72057594042384384"> 
    <owner-list> 
    <owner id="process6aa7a8" mode="X"/> 
    </owner-list> 
    <waiter-list> 
    <waiter id="process989018" mode="U" requestType="wait"/> 
    </waiter-list> 
    </keylock> 
    <keylock hobtid="72057594042384384" dbid="5" objectname="MESSAGESTOREDB61.dbo.part_data" indexname="idx_part_data_part_id" id="lock3cad340" mode="X" associatedObjectId="72057594042384384"> 
    <owner-list> 
    <owner id="process989018" mode="X"/> 
    </owner-list> 
    <waiter-list> 
    <waiter id="process6aa7a8" mode="U" requestType="wait"/> 
    </waiter-list> 
    </keylock> 
    </resource-list> 
</deadlock> 
</deadlock-list> 
+0

Hai una cancellazione a cascata da qualche parte? –

+0

Ciao Stefan. No, non ho vincoli di eliminazione a cascata. – Brad

+0

formato di schermata Web di non importa, copia/incolla in un file .XML locale e aprilo e sarà bello –

risposta

0

Sto assumendo che è stato eseguito qualcosa di simile: DBCC TRACEON (1222, -1) "e/o" DBCC TRACEON (1204, -1) . Trovo la traccia di deadlock nei log di SQLServer difficile da leggere. Sei sicuro che sia un tentativo di inoltrarsi a serrature esclusive (X)?

Provare a eseguire una traccia nel profiler (selezionare il modello vuoto), selezionare l'evento "deadlock graph" e nella nuova scheda visualizzata (Impostazioni di estrazione eventi), salvare ciascuno (selezionare "salva eventi deadlock XML separatamente ") nel proprio file.Apri questo file in un visualizzatore xml e sarà facile dire cosa sta succedendo. Ogni processo è contenuto, con uno stack di esecuzione di chiamate procedure/trigger/etc, ecc. E anche tutti i blocchi sono presenti. Trovo difficile credere che i due DELETE in cui col = @ valore stanno causando il problema, guarda lo stack di esecuzione, c'è un trigger o qualcos'altro che accade?

Guardare la sezione "Elenco risorse" del file, mostrerà ciò che viene bloccato e trattenuto da ogni processo che causa il deadlock. Scopri come non avere uno di questi bloccati e il deadlock sarà risolto.

+0

Ciao, grazie per il commento. Ho usato il profiler in precedenza, ma ora vedo che stavo interpretando l'output in modo errato. Dall'elenco delle risorse XML appare che i due client hanno già dei blocchi (X) e in effetti richiedono i blocchi (U). Dalla pila di esecuzione, le uniche cose che mostrano sono le due istruzioni di cancellazione. – Brad

+0

come possono eliminare le eliminazioni in ogni processo? multiplo o solo quello singolo per ogni processo? Se si eseguono più eliminazioni in un ciclo (con una transazione) in ogni processo, è possibile che si verifichino problemi di blocco perché i blocchi non si trovano su righe ma su pagine di più righe. per favore modifica la tua domanda e includi il deadlock graph xml così posso vedere meglio cosa stai vedendo. –

+0

La transazione sta eseguendo un'eliminazione su un grafico di oggetti che vanno insieme per costituire un'unità logica di dati. La tabella in cui l'eliminazione ha esito negativo corrisponde all'oggetto più in basso in questo grafico. È probabile che ci siano più di uno di questi oggetti ma un'eliminazione viene eseguita una sola volta a questo livello in quanto è un "delete where foreign key =?". Sono ancora curioso di sapere perché il deadlock sta accadendo sull'indice piuttosto che sul tavolo stesso. – Brad

2

deadlock in SQLServer derivano quasi sempre dal fatto che un singolo thread tenta di scrivere e leggere utilizzando due connessioni e quindi due transazioni. Se vuoi farlo funzionare, esegui tutte le operazioni in un singolo thread usando UNA connessione e assicurati che stai effettivamente riutilizzando la connessione. Potrebbe essere dovuto al layering nella tua applicazione che stai usando accidentalmente una connessione diversa per la lettura durante una transazione che fa sì che la lettura attenda l'altro codice (usando un'altra connessione) per completare il quale non accade mai mentre la lettura non finisce mai.

Esempio (gradini pseudo)

avviare trans

  • dati di scrittura (ad esempio INSERT, UPDATE) della tabella Foo
  • leggere alcuni dati (che innesca tabella di scansione) da Foo utilizzando diverso connessione DEADLOCK, come letto non finisce mai, quindi transactio non viene mai commesso
+0

Frans, questo è un punto interessante. Il materiale JDBC viene eseguito tramite il modello JDBC di Spring, quindi in realtà non sto gestendo alcuna connessione, ma è possibile che una lettura venga eseguita all'interno di una transazione accanto a un aggiornamento o eliminazione.Tornerò indietro e cercherò il tipo di situazione che hai descritto – Brad

+0

Dopo alcune indagini ulteriori ho notato che una delle transazioni sospette ha un inserto mescolato con le eliminazioni. La mia applicazione utilizza quattro classi di servizio per l'accesso al database che raggruppano le operazioni come letture, aggiornamenti, inserimenti ed eliminazioni. Immagino che il problema potrebbe essere che l'inserto usa una connessione diversa per le eliminazioni come avviene in una classe di template JDBC separata. Pensavo che la gestione delle transazioni di Spring avrebbe forzato le operazioni a utilizzare un'unica connessione, forse ho sbagliato? – Brad

+0

Non ho mai lavorato con Spring quindi non ho idea se riutilizza una transazione in corso, potrei immaginarlo, ma potrei anche immaginare che non lo sarebbe (se per esempio ti connetti a un db diverso, DEVI crearne uno nuovo) –

4

Benvenuti in orribile.

L'ultima volta che mi sono imbattuto in una situazione come questa era perché un aggiornamento o una cancellazione non era in grado di trovare un buon indice per aiutarlo a isolare le righe che stava influenzando. Ciò ha causato un'escalation di blocco irregolare poiché utilizzava un indice non di copertura per modificare i record.

Quindi, se è possibile isolare alcune query, controllare la sql per loro e vedere se non è possibile provare a fornire alcuni indici di copertura.

Un indice di copertura è un indice che include tutti i campi nella clausola specifica where.

+0

Nessun indice di copertura sarebbe d'aiuto, ma un buon indice di copertura include tutte le colonne che devono essere restituite. Se si seleziona una cola, il colb di table1 ... cola e colb dovrebbe essere nell'indice di copertura in modo che SQL Server non debba tornare alla riga originale per ottenere i dati. –

0

... ho deciso di fare tutte le letture sul tavolo in questione essere fatto "per l'aggiornamento" utilizzando l'hint UPDLOCK in modo che la situazione di stallo potrebbe essere evitato ...

... Sì, esiste una transazione dichiarata a per garantire che l'intero grafico dell'oggetto sia eliminato o lasciato intatto in caso di errore . Non ci sono altre di database operazioni nella transazione, alcuni letture sono fatte prima che la transazione viene avviata ma nessuno al suo interno ...

io non sono sicuro di aver capito il ragionamento dietro l'escalation dei blocchi di lettura al di fuori la transazione, direi che questo non farebbe che esacerbare il problema. La mia esperienza è stata che specificare anticipatamente i suggerimenti di chiusura causa più danni che benefici.

Detto questo ...

Sembra che si sta tentando di eliminare più righe all'interno di ogni transazione e che il deadlocking si sta verificando perché SQL si sta aggravando il livello di blocco sul tavolo per ciascuno. Ricordare che in SQL Server Row, Key-Range e Page lock tutti escalation immediatamente a Table Locks. Se hai più transazioni che eliminano più righe, tenteranno di intensificarle e otterrai deadlock.

Questo ucciderà prestazioni se si dispone di molti utenti, ma provare a specificare suggerimenti TABLOCK sulle istruzioni DELETE e vedere se il problema va via.

prendere anche uno sguardo ai piani di esecuzione di istruzioni SQL a vedere come i escalation di blocco si verificano.

1

Per prima cosa non utilizzare i suggerimenti, in genere SQL Server è meglio lasciarlo da solo.

Secondo verificare che non si dispone di una situazione di stallo REAL (ma succede molto meno spesso della comparsa stallo può suggerire).

In terzo luogo, come qualcuno ha suggerito, controllare se hai un po 'di query lente e sintonizzare.

Nella mia esperienza ogni volta un cliente ha segnalato una situazione di stallo questo è accaduto perché c'era una query lenta esecuzione che si intensifica e mai una vera e propria situazione di stallo e di messa a punto la query o l'aggiunta di un indice specifico sempre risolto il problema .

non vedere quale versione di SQL Server si sta parlando ma ogni seguente versione ha una migliore gestione stallo di quello precedente e SQL Server 2000 è stato particolarmente brutto in questo argomento.

saluti
Massimo

+0

Ciao Massimo, grazie per la tua risposta. Un po 'impegnato al momento, ma lo rivisiterò presto e tornerò da te. – Brad

0

ho incontrato lo stesso problema molti anni fa su un tavolo mirato di oltre un miliardo di transazioni al giorno.

Avevamo quattro servizi tomcat per gestire le query di un sito web. A volte, due servizi distinti stavano cercando di trattare la stessa query, che appare quando il traffico del sito Web era al minimo. L'operazione di eliminazione della prima query stava bloccando il IX e la seconda query, eseguita nello stesso momento, stava facendo la stessa cosa.

Abbiamo risolto questo problema creando una tabella di buffer. Le azioni di cancellazione sono state memorizzate in quella tabella. Ogni secondo, un lavoro sql legge quella tabella per cancellare le righe contrassegnate come "da cancellare".

0

Prima di tutto sono due istruzioni di eliminazione. Queste due SPID sono in esecuzione nella stessa istruzione

delete from part_data where part_id = @P0

e probabilmente non sarà tentando di eliminare lo stesso record. Qui si verifica un deadlock perché SQLServer non è in grado di identificare la riga/le righe dalla tabella part_data utilizzando la colonna part_id e blocca anche altri record.

in modo da avere due opzioni

  1. Fai part_id sua chiave primaria (se possibile)

    o

  2. Alter l'istruzione come segue. (Supponendo che ci sia una chiave primaria)

    Select primary_key into #temp From part_data where part_id = @p0

    - otterrete la chiave primaria di quei dischi che devono essere eliminati

    delete a from part_data a join #temp b on a.primary_key = b.primary_key

- È possibile cancella i record che devono essere cancellati senza bloccare altri record