¿Por qué UPDLOCK hace que los SELECT se cuelguen (bloqueen)?

13

Tengo una selección en SQL SERVER que bloquea toda la tabla.

Aquí está el script de configuración (asegúrese de no sobrescribir nada)

USE [master]
GO

IF EXISTS(SELECT 1 FROM sys.databases d WHERE d.name = 'LockingTestDB')
DROP DATABASE LockingTestDB
GO

CREATE DATABASE LockingTestDB
GO

USE [LockingTestDB]
GO
IF EXISTS(SELECT 1 FROM sys.tables t WHERE t.name = 'LockingTestTable')
  DROP TABLE LockingTestTable
GO

CREATE TABLE LockingTestTable (
  Id int IDENTITY(1, 1),
  Name varchar(100),
  PRIMARY KEY CLUSTERED (Id)
)
GO

INSERT INTO LockingTestTable(Name) VALUES ('1')
INSERT INTO LockingTestTable(Name) VALUES ('2')
GO

Abra una nueva ventana de consulta y ejecute la siguiente transacción (que tiene una espera):

USE [LockingTestDB]
GO

BEGIN TRANSACTION
  SELECT * FROM LockingTestTable t WITH (UPDLOCK, ROWLOCK) WHERE t.Name = '1'
  WAITFOR DELAY '00:01:00'

COMMIT TRANSACTION
--ROLLBACK
GO

USE [master]
GO

Y otro que se ejecutará (asegúrese de que se ejecuten al mismo tiempo):

USE [LockingTestDB]
GO

SELECT * FROM LockingTestTable t WITH (UPDLOCK, ROWLOCK) WHERE t.Name = '2'

USE [master]
GO

Notarás que la segunda consulta será bloqueada por la primera. Detenga la primera consulta y ejecute ROLLBACK y se completará la segunda.

¿Por qué está pasando esto?

PD: Agregar un índice no agrupado (con cobertura completa) sobre Nombre lo solucionará:

USE [LockingTestDB]
GO

CREATE NONCLUSTERED INDEX [IX_Name] ON [dbo].[LockingTestTable] 
(
  [Name] ASC
)
INCLUDE ( [Id]) WITH (STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

De nuevo por qué?

marius-O
fuente

Respuestas:

19

Como se documenta en Books Online , UPDLOCKtoma bloqueos de actualización y los retiene hasta el final de la transacción.

Sin un índice para ubicar las filas que se bloquearán, todas las filas probadas se bloquean y los bloqueos en las filas que califican se mantienen hasta que se complete la transacción.

La primera transacción tiene un bloqueo de actualización en la fila donde nombre = 1. La segunda transacción se bloquea cuando intenta adquirir un bloqueo de actualización en la misma fila (para probar si nombre = 2 para esa fila).

Con un índice, SQL Server puede localizar y bloquear rápidamente solo aquellas filas que califican, por lo que no hay conflicto.

Debe revisar el código con un profesional de bases de datos calificado para validar el motivo de la sugerencia de bloqueo y para asegurarse de que estén presentes los índices adecuados.

Información relacionada: Modificaciones de datos en Aislamiento de instantáneas confirmadas de lectura

Paul White 9
fuente