Почему строка выравнивается, блокировка не, кажется, работают правильно в SQL-сервере?

Это - продолжение от того, Когда я обновляю/вставляю одну строку, это должно заблокировать всю таблицу?

Вот моя проблема.

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

То, когда я получаю доступ к записи в этом, блокирует таблицу, я хочу смочь заблокировать ее и обновить ее (просто одна запись) без любой другой способности процесса сделать то же. Я могу сделать это с подсказкой блокировки, такой как updlock.

Что происходит, хотя то, что даже при том, что я использую блокировку строки для блокировки записи, она блокирует запрос к другому процессу для изменения абсолютно несвязанной строки в той же таблице, которая также указала бы подсказку updlock наряду с блокировкой строки.

Можно воссоздать это сделать таблицу

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Locks](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [LockName] [varchar](50) NOT NULL,
    [Locked] [bit] NOT NULL,
 CONSTRAINT [PK_Locks] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 100) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[Locks] ADD  CONSTRAINT [DF_Locks_LockName]  DEFAULT ('') FOR [LockName]
GO
ALTER TABLE [dbo].[Locks] ADD  CONSTRAINT [DF_Locks_Locked]  DEFAULT ((0)) FOR [Locked]
GO

Добавьте две строки для блокировки с LockName ='A’ и один для LockName ='B’

Затем создайте два запроса для выполнения в транзакции одновременно против него:

Запрос 1:

Commit
Begin transaction
select * From Locks with (updlock rowlock) where LockName='A'

Запрос 2:

select * From Locks with (updlock rowlock) where LockName='B'

Обратите внимание на то, что я оставляю транзакцию открытой так, чтобы Вы видели эту проблему, так как это не было бы видимо без этой открытой транзакции.

Когда, блокировки выполнения запросов 1 являются проблемами для строки и любыми последующими запросами для LockName ='A’, необходимо будет ожидать. Это поведение корректно.

То, где это становится немного печальным, - когда Вы, выполнение запросов 2, Вы заблокированы до Запроса 1 конец даже, думало, что это несвязанные записи. Если Вы затем выполнение запросов 1 снова так же, как у меня есть он выше, это будет фиксировать предыдущую транзакцию, Запрос 2 будет работать, и затем Запрос 1 еще раз заблокирует запись.

Предложите некоторые предложения относительно того, как я смог иметь его, правильно блокируют ТОЛЬКО одну строку и не препятствуют тому, чтобы другие объекты были обновлены также.

PS. Holdlock также не удается произвести корректное поведение после того, как одна из строк будет обновлена.

5
задан Community 23 May 2017 в 12:03
поделиться

4 ответа

В SQL Server подсказки блокировки применяются к отсканированным, не сопоставленным объектам.

Обычно механизм устанавливает общую блокировку объектов (страниц и т. Д.) Во время их чтения и снимает их (или не снимает в транзакциях SERIALIZABLE ) после завершения сканирования.

Однако вы указываете движку установить (и снять) блокировки обновления, которые несовместимы друг с другом.

Транзакция B блокируется при попытке поместить UPDLOCK в строку, уже заблокированную с помощью UPDLOCK транзакцией A .

Если вы создаете индекс и принудительно используете его (чтобы не возникало конфликтующих чтений), ваши таблицы не будут заблокированы:

CREATE INDEX ix_locks_lockname ON locks (lockname)

Begin transaction
select * From Locks with (updlock rowlock INDEX (ix_locks_lockname)) where LockName='A'

Begin transaction
select * From Locks with (updlock rowlock INDEX (ix_locks_lockname)) where LockName='B'
10
ответ дан 18 December 2019 в 14:45
поделиться

Для запроса 2 попробуйте использовать подсказку READPAST - this (quote):

Указывает, что компонент Database Engine не читает строки, заблокированные другими сделки. В большинстве случаев то же самое верно для страниц . Если указано READPAST, блокировки как на уровне строки, так и на уровне страницы пропускаются. То есть механизм базы данных пропускает строки или страницы вместо того, чтобы блокировать текущую транзакцию, пока блокировки не будут сняты

Обычно это используется в средах с обработкой очередей - чтобы несколько процессов могли вытащить следующий элемент из таблицы очереди, не блокируясь другими процессами (конечно, с помощью UPDLOCK, чтобы предотвратить выбор одной и той же строки несколькими процессами).

Редактировать 1:
Это могло произойти, если у вас нет индекса в поле LockName. С помощью индекса запрос 2 может выполнить поиск по индексу до конкретной строки. Но без него он будет сканировать (проверять каждую строку), что означает, что его задерживает первая транзакция. Так что, если он не проиндексирован, попробуйте проиндексировать его.

1
ответ дан 18 December 2019 в 14:45
поделиться

Я не уверен, чего вы пытаетесь достичь, но обычно те, кто имеет дело с подобными проблемами, хотят использовать sp_getapplock. Описано Тони Роджерсоном:Assisting Concurrency by creating your own Locks (Mutexs in SQL)

1
ответ дан 18 December 2019 в 14:45
поделиться

Если вы хотите поставить в очередь в SQL Server, используйте подсказки UPDLOCK, ROWLOCK, READPAST . Оно работает.

Я бы подумал об изменении вашего подхода, вместо того, чтобы пытаться изменить поведение SQL Server ...

1
ответ дан 18 December 2019 в 14:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: