Чому UPDLOCK викликає зависання (блокування) SELECTs?


13

У SQL SERVER у мене вибір, який фіксує всю таблицю.

Ось сценарій налаштування (переконайтеся, що ви нічого не перезаписуєте)

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

Відкрийте нове вікно запитів та запустіть таку транзакцію (у якій є очікування):

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

І ще одна, яка працюватиме (переконайтеся, що вони працюють одночасно):

USE [LockingTestDB]
GO

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

USE [master]
GO

Ви помітите, що другий запит буде заблокований першим. Зупиніть перший запит і виконайте ROLLBACK, а другий буде завершено.

Чому це відбувається?

PS: Додавання некластеризованого індексу (з повним покриттям) над Name виправить це:

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

Знову чому?

Відповіді:


19

Як задокументовано в Books Online , UPDLOCKзнімає оновлені блокування та тримає їх до кінця транзакції.

Без індексу для пошуку рядків, які слід заблокувати, усі перевірені рядки блокуються, а блоки в кваліфікованих рядках зберігаються до завершення транзакції.

Перша транзакція містить блокування оновлення в рядку, де name = 1. Друга транзакція блокується, коли вона намагається придбати блокування оновлення для того самого рядка (перевірити, чи ім'я = 2 для цього рядка).

За допомогою індексу SQL Server може швидко знаходити та фіксувати лише ті рядки, які відповідають вимогам, тому немає конфлікту.

Ви повинні ознайомитись з кодом у кваліфікованого фахівця бази даних, щоб перевірити причину підказки блокування та переконатися в наявності відповідних індексів.

Суміжні відомості: Модифікації даних у розділі "Прочитання виконуваного знімка"

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.