Почему UPDLOCK вызывает зависание SELECT (блокировка)?


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: добавление некластеризованного индекса (с полным охватом) поверх имени исправит это:

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блокировка обновления выполняется и удерживает их до конца транзакции.

Без индекса для определения местоположения строк, которые должны быть заблокированы, все проверенные строки блокируются, и блокировки в соответствующих строках удерживаются до завершения транзакции.

Первая транзакция содержит блокировку обновления для строки, где имя = 1. Вторая транзакция блокируется, когда она пытается получить блокировку обновления для той же строки (для проверки, если имя = 2 для этой строки).

С помощью индекса SQL Server может быстро находить и блокировать только те строки, которые соответствуют требованиям, поэтому конфликта нет.

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

Информация, связанная с данной: Модификации данных в разделе Read Committed Snapshot Isolation

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.