Вы уже задавали этот вопрос и получили ответ: исправьте свою схему и свой код . В этом посте конфликт блокировок был блокировкой IX, а конфликт блокировок намерений указывает на блокировки с высокой степенью детализации, что, в свою очередь, указывает на сканирование таблицы. Вам не нужны подсказки блокировки, вам нужен только индекс и достойные запросы. Возьмем, к примеру, ваш другой вопрос: Почему блокировка на уровне строк не работает правильно на SQL-сервере? , где ответ тривиален: таблицу блокировок необходимо организовать по кластерному индексу LockName:
CREATE TABLE [dbo].[Locks](
[LockName] [varchar](50) NOT NULL,
[Locked] [bit] NOT NULL,
CONSTRAINT [PK_Locks] PRIMARY KEY CLUSTERED ([LockName]));
GO
insert into Locks (LockName, Locked) values ('A', 0);
insert into Locks (LockName, Locked) values ('B', 0);
GO
На одном занятии сделайте это:
begin transaction
update Locks
set Locked=1
output inserted.*
where LockName = 'A';
На другом сеансе сделайте это:
begin transaction
update Locks
set Locked=1
output inserted.*
where LockName = 'B';
Нет конфликта обновлений, нет блокировки, нет необходимости (неправильных) подсказок, ничего. Просто хорошая оле 'правильная схема и дизайн запроса.
В качестве примечания: описанная здесь блокировка уже существует и называется блокировкой клавиш. Это стандартный неявный режим работы SQL Server. Как, по-вашему, в мире SQL Server может публиковать показатели производительности TPC-C, равные 16000 транзакций в секунду? У вас есть все возможности параллелизма на сервере, вам просто нужно прочитать одну или две книги, чтобы понять, как их использовать. Существует множество литературы по этому вопросу, которую можно начать с Обработка транзакций: концепции и методы .
Обновлено
begin transaction
select lockname from locks where lockname='A'
update Locks Set locked=1 where lockname='A'
Это никогда не сработает, независимо от того, сколько / разных подсказок блокировки вы пытаетесь. Вот почему у вас есть обновление с синтаксисом вывода:
begin transaction
update Locks
Set locked=1
output inserted.*
where lockname='A'
это гарантирует, что вы сначала обновите, а затем вернете то, что обновили. Этот метод довольно распространен в базах данных именно для той семантики, которую вы ищете: получение ресурсов. Фактически эта техника является краеугольным камнем дочернего плаката получения ресурсов: обработка очереди. См. Queues параграф в OUTPUT Clause . В очередях у вас есть таблица ресурсов для обработки, и каждый поток захватывает ее, блокирует и запускает обработку:
create table Resources (
id int identity(1,1) not null,
enqueue_time datetime not null default getutcdate(),
is_processing bit not null default 0,
payload xml);
create clustered index cdxResources on Resources
(is_processing, enqueue_time);
go
-- enqueue:
insert into Resources (payload) values ('<do>This</do>');
insert into Resources (payload) values ('<do>That</do>');
insert into Resources (payload) values ('<do>Something</do>');
insert into Resources (payload) values ('<do>Anything</do>');
Теперь из отдельных сеансов запустите это:
--dequeue
begin transaction;
with cte as (
select top(1) *
from Resources with(readpast)
where is_processing = 0
order by enqueue_time)
update cte
set is_processing = 1
output inserted.*;
Вы увидите, что каждая сессия захватывает свой собственный ресурс, блокирует его и пропускает все, что заблокировано всеми остальными. Так получилось, что у меня в работе работает система, которая работает точно так же, с более чем 5M ресурсами в таблице (это запросы на обработку платежей через веб-сервис), и выгрузкой из очереди и обработкой около 50 в секунду из 100 параллельных процессоров (занимает около 2 секунд). за вызов к процессу). На кусок ненужного оборудования. Так что это абсолютно возможно.