Warum führt UPDLOCK dazu, dass SELECTs hängen bleiben (sperren)?

13

Ich habe eine Auswahl in SQL Server, die die gesamte Tabelle sperrt.

Hier ist das Setup-Skript (stellen Sie sicher, dass Sie nichts überschreiben)

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

Öffnen Sie ein neues Abfragefenster und führen Sie die folgende (wartende) Transaktion aus:

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

Und noch eine, die ausgeführt wird (stellen Sie sicher, dass sie zur gleichen Zeit ausgeführt werden):

USE [LockingTestDB]
GO

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

USE [master]
GO

Sie werden feststellen, dass die zweite Abfrage von der ersten Abfrage blockiert wird. Stoppen Sie die erste Abfrage und führen Sie den ROLLBACK aus. Die zweite Abfrage wird abgeschlossen.

Warum passiert dies?

PS: Durch Hinzufügen eines nicht gruppierten Index (mit vollständiger Abdeckung) über Name wird Folgendes behoben:

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

Wieder warum?

Marius-O
quelle

Antworten:

19

Wie in der Onlinedokumentation dokumentiert , werden UPDLOCKAktualisierungssperren verwendet und bis zum Ende der Transaktion beibehalten.

Ohne einen Index zum Auffinden der zu sperrenden Zeile (n) werden alle getesteten Zeilen gesperrt, und Sperren für qualifizierende Zeilen werden beibehalten, bis die Transaktion abgeschlossen ist.

Die erste Transaktion enthält eine Aktualisierungssperre für die Zeile, in der name = 1 ist. Die zweite Transaktion wird blockiert, wenn versucht wird, eine Aktualisierungssperre für dieselbe Zeile zu erlangen (um zu testen, ob name = 2 für diese Zeile ist).

Mit einem Index kann SQL Server schnell nur die qualifizierten Zeilen finden und sperren, sodass kein Konflikt besteht.

Sie sollten den Code mit einem qualifizierten Datenbankexperten überprüfen, um den Grund für den Sperrhinweis zu überprüfen und sicherzustellen, dass geeignete Indizes vorhanden sind.

Zugehörige Informationen: Datenänderungen unter Read Committed Snapshot Isolation

Paul White 9
quelle