Mengapa UPDLOCK menyebabkan SELECT menggantung (mengunci)?

13

Saya punya pilihan dalam SQL SERVER yang mengunci seluruh tabel.

Inilah skrip pengaturan (pastikan Anda tidak menimpa apa pun)

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

Buka jendela permintaan baru dan jalankan transaksi berikut (yang sudah menunggu):

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

Dan satu lagi yang akan berjalan (pastikan mereka berjalan pada saat yang sama):

USE [LockingTestDB]
GO

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

USE [master]
GO

Anda akan melihat permintaan kedua akan diblokir oleh yang pertama. Hentikan permintaan pertama dan jalankan ROLLBACK dan yang kedua akan selesai.

Mengapa ini terjadi?

PS: Menambahkan indeks yang tidak berkerumun (dengan cakupan penuh) di atas Nama akan memperbaikinya:

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

Lagi kenapa?

marius-O
sumber

Jawaban:

19

Seperti yang didokumentasikan dalam Buku Online , UPDLOCKambil kunci pembaruan dan tahan hingga akhir transaksi.

Tanpa indeks untuk menemukan baris yang akan dikunci, semua baris yang diuji dikunci, dan kunci pada baris yang memenuhi syarat ditahan sampai transaksi selesai.

Transaksi pertama memegang kunci pembaruan pada baris di mana nama = 1. Transaksi kedua diblokir ketika mencoba untuk mendapatkan kunci pembaruan di baris yang sama (untuk menguji apakah nama = 2 untuk baris itu).

Dengan indeks, SQL Server dapat dengan cepat menemukan dan mengunci hanya baris-baris yang memenuhi syarat, sehingga tidak ada konflik.

Anda harus meninjau kode dengan profesional basis data yang berkualifikasi untuk memvalidasi alasan penguncian petunjuk, dan untuk memastikan indeks yang sesuai ada.

Informasi terkait: Modifikasi Data di bawah Read Committed Snapshot Isolasi

Paul White 9
sumber