您之前曾问过这个问题并得到了答案:修复您的架构和代码。在那篇文章中,锁冲突是 IX 锁,意图锁的冲突表示高粒度锁,这反过来又表示表扫描。你不需要锁定提示,你只需要一个索引和体面的查询。以您的另一个问题为例,为什么行级锁定在 SQL Server 中似乎无法正常工作?答案很简单:锁表需要由 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';
没有更新冲突,没有阻塞,不需要(错误的)提示,什么都没有。只是好的 ole' 正确的架构和查询设计。
作为旁注,您在此处描述的锁已经存在,称为钥匙锁。它们是 SQL Server 运行的默认隐式模式。您如何想象 SQL Server 可以发布每秒 16000 tpc 事务的 TPC-C 基准数?您拥有服务器中所需的所有并行能力,您只需要阅读一两本书即可了解如何使用它。关于这个主题有很多文献,您可以从事务处理:概念和技术开始。
更新
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'
这确保您首先更新,然后返回您更新的内容。这种技术在数据库中非常常见,正是您所寻求的语义:资源获取。事实上,这种技术是资源获取典型代表的基石:队列处理。请参阅OUTPUT 子句中的队列段落。在队列中,您有一张要处理的资源表,每个线程抓取一个,锁定它并开始处理:
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.*;
您会看到每个会话都获取它自己的资源,将其锁定并跳过其他人锁定的所有内容。碰巧我在生产中拥有一个运行完全一样的系统,表中有超过 500 万个资源(它们是 Web 服务支付处理请求),并且从 100 个并发处理器(大约需要 2 秒。每个呼叫处理)。在一块垃圾硬件上。所以绝对有可能。