Estoy resolviendo un problema de bloqueo mientras noté que el comportamiento de bloqueo es diferente cuando uso el índice agrupado y no agrupado en el campo id. El problema de punto muerto parece resolverse si el índice agrupado o la clave primaria se aplican al campo id.
Tengo diferentes transacciones haciendo una o más actualizaciones a diferentes filas, por ejemplo, la transacción A solo actualizará la fila con ID = a, tx B solo tocará la fila con ID = b, etc.
Y tengo entendido que sin índice, la actualización adquirirá un bloqueo de actualización para todas las filas y se convertirá en un bloqueo exclusivo cuando sea necesario, lo que eventualmente conducirá a un punto muerto. Pero no entiendo por qué con el índice no agrupado, el punto muerto sigue ahí (aunque la tasa de aciertos parece haberse reducido)
Tabla de datos:
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
)
Rastro de punto muerto
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
También un hallazgo interesante y posible relacionado es que el índice agrupado y no agrupado parece tener diferentes comportamientos de bloqueo
Cuando se usa el índice agrupado, hay un bloqueo exclusivo en la clave, así como un bloqueo exclusivo en RID cuando se actualiza, lo que se espera; mientras que hay dos bloqueos exclusivos en dos RID diferentes si se utiliza un índice no agrupado, lo que me confunde.
Sería útil si alguien puede explicar por qué en esto también.
Prueba SQL:
use SQL2012_707688_webows;
begin transaction;
update [user] with (rowlock) set todoOrder='{1}' where id = 63501
exec sp_lock;
commit;
Con id como índice agrupado:
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 como índice no agrupado
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
EDITAR1: Detalles del punto muerto sin ningún índice.
Digamos que tengo dos tx A y B, cada uno con dos declaraciones de actualización, una fila diferente, por supuesto,
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} y {4} tendrían una posibilidad de punto muerto, ya que
en {1}, se solicita el bloqueo U para la fila 63502 ya que necesita hacer un escaneo de la tabla, y el bloqueo X podría haberse mantenido en la fila 63501 ya que coincide con la condición
en {4}, se solicita el bloqueo U para la fila 63501 y el bloqueo X ya se mantiene para 63502
entonces tenemos txA contiene 63501 y espera 63502 mientras que txB contiene 63502 esperando 63501, que es un punto muerto
EDIT2: Resulta que un error de mi caso de prueba hace una situación diferente aquí Perdón por la confusión, pero el error hace una situación diferente, y parece causar el punto muerto eventualmente.
Como el análisis de Paul realmente me ayudó en este caso, lo aceptaré como respuesta.
Debido al error de mi caso de prueba, dos transacciones txA y txB pueden actualizar la misma fila, como se muestra a continuación:
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} y {3} tendrían una posibilidad de punto muerto cuando:
txA solicita bloqueo U en la tecla mientras mantiene el bloqueo X en RID (debido a la actualización de {1}) txB solicita bloqueo U en RID mientras mantiene el bloqueo U en la tecla