Sto risolvendo un problema di deadlock mentre ho notato che il comportamento del blocco è diverso quando utilizzo l'indice cluster e non cluster sul campo ID. Il problema del deadlock sembra essere risolto se l'indice cluster o la chiave primaria vengono applicati al campo ID.
Ho transazioni diverse che fanno uno o più aggiornamenti su righe diverse, ad esempio la transazione A aggiornerà la riga solo con ID = a, tx B toccherà la riga solo con ID = b ecc.
E ho capito che senza indice, l'aggiornamento acquisirà il blocco degli aggiornamenti per tutte le righe e si convertirà in blocco esclusivo quando necessario, che alla fine porterà a deadlock. Ma non riesco a scoprire perché con l'indice non cluster, il deadlock è ancora lì (anche se la percentuale di hit sembra essere diminuita)
Tabella dati:
CREATE TABLE [dbo].[user](
[id] [int] IDENTITY(1,1) NOT NULL,
[userName] [nvarchar](255) NULL,
[name] [nvarchar](255) NULL,
[phone] [nvarchar](255) NULL,
[password] [nvarchar](255) NULL,
[ip] [nvarchar](30) NULL,
[email] [nvarchar](255) NULL,
[pubDate] [datetime] NULL,
[todoOrder] [text] NULL
)
Traccia deadlock
deadlock-list
deadlock victim=process4152ca8
process-list
process id=process4152ca8 taskpriority=0 logused=0 waitresource=RID: 5:1:388:29 waittime=3308 ownerId=252354 transactionname=user_transaction lasttranstarted=2014-04-11T00:15:30.947 XDES=0xb0bf180 lockMode=U schedulerid=3 kpid=11392 status=suspended spid=57 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2014-04-11T00:15:30.953 lastbatchcompleted=2014-04-11T00:15:30.950 lastattention=1900-01-01T00:00:00.950 clientapp=.Net SqlClient Data Provider hostname=BOOD-PC hostpid=9272 loginname=getodo_sql isolationlevel=read committed (2) xactid=252354 currentdb=5 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056
executionStack
frame procname=adhoc line=1 stmtstart=62 sqlhandle=0x0200000062f45209ccf17a0e76c2389eb409d7d970b0f89e00000000000000000000000000000000
update [user] WITH (ROWLOCK) set [todoOrder]=@para0 where id=@owner
frame procname=unknown line=1 sqlhandle=0x00000000000000000000000000000000000000000000000000000000000000000000000000000000
unknown
inputbuf
(@para0 nvarchar(2)<c/>@owner int)update [user] WITH (ROWLOCK) set [todoOrder]=@para0 where id=@owner
process id=process4153468 taskpriority=0 logused=4652 waitresource=KEY: 5:72057594042187776 (3fc56173665b) waittime=3303 ownerId=252344 transactionname=user_transaction lasttranstarted=2014-04-11T00:15:30.920 XDES=0x4184b78 lockMode=U schedulerid=3 kpid=7272 status=suspended spid=58 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2014-04-11T00:15:30.960 lastbatchcompleted=2014-04-11T00:15:30.960 lastattention=1900-01-01T00:00:00.960 clientapp=.Net SqlClient Data Provider hostname=BOOD-PC hostpid=9272 loginname=getodo_sql isolationlevel=read committed (2) xactid=252344 currentdb=5 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056
executionStack
frame procname=adhoc line=1 stmtstart=60 sqlhandle=0x02000000d4616f250747930a4cd34716b610a8113cb92fbc00000000000000000000000000000000
update [user] WITH (ROWLOCK) set [todoOrder]=@para0 where id=@uid
frame procname=unknown line=1 sqlhandle=0x00000000000000000000000000000000000000000000000000000000000000000000000000000000
unknown
inputbuf
(@para0 nvarchar(61)<c/>@uid int)update [user] WITH (ROWLOCK) set [todoOrder]=@para0 where id=@uid
resource-list
ridlock fileid=1 pageid=388 dbid=5 objectname=SQL2012_707688_webows.dbo.user id=lock3f7af780 mode=X associatedObjectId=72057594042122240
owner-list
owner id=process4153468 mode=X
waiter-list
waiter id=process4152ca8 mode=U requestType=wait
keylock hobtid=72057594042187776 dbid=5 objectname=SQL2012_707688_webows.dbo.user indexname=10 id=lock3f7ad700 mode=U associatedObjectId=72057594042187776
owner-list
owner id=process4152ca8 mode=U
waiter-list
waiter id=process4153468 mode=U requestType=wait
Inoltre, una scoperta interessante e possibile correlata è che l'indice cluster e non cluster sembra avere comportamenti di blocco diversi
Quando si utilizza l'indice cluster, è presente un blocco esclusivo sulla chiave e un blocco esclusivo su RID durante l'aggiornamento, che è previsto; mentre ci sono due blocchi esclusivi su due RID diversi se viene utilizzato un indice non cluster, il che mi confonde.
Sarebbe utile se qualcuno potesse spiegare perché anche su questo.
Test SQL:
use SQL2012_707688_webows;
begin transaction;
update [user] with (rowlock) set todoOrder='{1}' where id = 63501
exec sp_lock;
commit;
Con ID come indice cluster:
spid dbid ObjId IndId Type Resource Mode Status
53 5 917578307 1 KEY (b1a92fe5eed4) X GRANT
53 5 917578307 1 PAG 1:879 IX GRANT
53 5 917578307 1 PAG 1:1928 IX GRANT
53 5 917578307 1 RID 1:879:7 X GRANT
Con ID come indice non cluster
spid dbid ObjId IndId Type Resource Mode Status
53 5 917578307 0 PAG 1:879 IX GRANT
53 5 917578307 0 PAG 1:1928 IX GRANT
53 5 917578307 0 RID 1:879:7 X GRANT
53 5 917578307 0 RID 1:1928:18 X GRANT
EDIT1: Dettagli di deadlock senza alcun indice
Dire che ho due tx A e B, ciascuno con due istruzioni di aggiornamento, riga diversa naturalmente
tx A
update [user] with (rowlock) set todoOrder='{1}' where id = 63501
update [user] with (rowlock) set todoOrder='{2}' where id = 63501
tx B
update [user] with (rowlock) set todoOrder='{3}' where id = 63502
update [user] with (rowlock) set todoOrder='{4}' where id = 63502
{1} e {4} avrebbero una possibilità di deadlock, da allora
su {1}, è richiesto il blocco U per la riga 63502 poiché deve eseguire una scansione della tabella e il blocco X potrebbe essere stato trattenuto sulla riga 63501 poiché corrisponde alla condizione
su {4}, il blocco U è richiesto per la riga 63501 e il blocco X è già in attesa per 63502
quindi abbiamo txA con 63501 e attende 63502 mentre txB con 63502 in attesa di 63501, che è un deadlock
EDIT2: Si scopre che un bug del mio caso di test fa una situazione di differenza qui Ci scusiamo per la confusione, ma il bug fa una situazione di differenza e sembra causare eventualmente il deadlock.
Dal momento che l'analisi di Paul mi ha davvero aiutato in questo caso, quindi lo accetterò come risposta.
A causa del bug del mio caso di test, due transazioni txA e txB possono aggiornare la stessa riga, come di seguito:
tx A
update [user] with (rowlock) set todoOrder='{1}' where id = 63501
update [user] with (rowlock) set todoOrder='{2}' where id = 63501
tx B
update [user] with (rowlock) set todoOrder='{3}' where id = 63501
{2} e {3} avrebbero la possibilità di un deadlock quando:
txA richiede il blocco U sulla chiave mentre tiene il blocco X sul RID (a causa dell'aggiornamento di {1}) txB richiede il blocco U sul RID mentre tiene il blocco U sulla chiave