web-dev-qa-db-fra.com

Pourquoi UPDLOCK fait-il bloquer (verrouiller) SELECT?

J'ai une sélection dans SQL SERVER qui verrouille toute la table.

Voici le script de configuration (assurez-vous de ne rien écraser)

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

Ouvrez une nouvelle fenêtre de requête et exécutez la transaction suivante (qui comporte une attente):

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

Et un autre qui s'exécutera (assurez-vous qu'ils s'exécutent en même temps):

USE [LockingTestDB]
GO

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

USE [master]
GO

Vous remarquerez que la deuxième requête sera bloquée par la première. Arrêtez la première requête et exécutez le ROLLBACK et la seconde se terminera.

Pourquoi cela arrive-t-il?

PS: L'ajout d'un index non clusterisé (avec une couverture complète) sur Nom le réparera:

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

Encore pourquoi?

13
marius-O

Comme documenté dans la documentation en ligne , UPDLOCK prend les verrous de mise à jour et les maintient jusqu'à la fin de la transaction.

Sans index pour localiser les lignes à verrouiller, toutes les lignes testées sont verrouillées et les verrous sur les lignes éligibles sont maintenus jusqu'à la fin de la transaction.

La première transaction contient un verrou de mise à jour sur la ligne où nom = 1. La deuxième transaction est bloquée lorsqu'elle tente d'acquérir un verrou de mise à jour sur la même ligne (pour tester si nom = 2 pour cette ligne).

Avec un index, SQL Server peut rapidement localiser et verrouiller uniquement les lignes qualifiées, il n'y a donc pas de conflit.

Vous devez examiner le code avec un professionnel de base de données qualifié pour valider la raison de l'indication de verrouillage et pour vous assurer que les index appropriés sont présents.

Information associée: Modifications des données sous Isolation de capture instantanée de lecture validée

19
Paul White 9