tablockx是否需要在每个会话中指定?

4
我有两个测试事务,分别在两个会话中。假设这两个事务将同时运行。我尝试的是让一个事务在另一个事务完成后正确插入发票号码。不重复。我按如下方式执行。但如果我在第二个会话中删除(tablockx),它们将无法工作。我在网上查看了书籍,但没有答案。请问是否有人可以帮忙?Serializable不起作用,因为这里两个SELECT想要彼此独占。谢谢。
在第一会话中:
begin transaction 
    declare @i int
    select @i=MAX(InvNumber) from Invoice 
    with(tablockx) 
    where LocName='A'
    waitfor delay '00:00:10'
    set @i=@i+1
    insert into Invoice values('A',@i);
commit 

在第二节课中:

begin transaction 
    declare @i int
    select @i=MAX(InvNumber) from Invoice 
    with(tablockx) 
    where LocName='A'
    set @i=@i+1
    insert into Invoice values('A',@i);
commit 
1个回答

1
那样做可以起作用,但也会完全阻止对表的所有其他访问。
如果使用 WITH(UPDLOCK, HOLDLOCK),则可以在比表更低的粒度和模式下进行锁定。
HOLDLOCK 提供可串行化语义,因此可以仅锁定索引顶部的范围(如果您在 LocName,InvNumber 上有索引)。
UPDLOCK 确保两个并发事务不能同时持有相同的锁,但与排他锁不同,它不会阻止没有使用提示的其他(普通)读取器。
BEGIN TRANSACTION

DECLARE @i INT

SELECT @i = MAX(InvNumber)
FROM   Invoice WITH(UPDLOCK, HOLDLOCK)
WHERE  LocName = 'A'

WAITFOR delay '00:00:10'

SET @i=@i + 1

INSERT INTO Invoice
VALUES     ('A',
            @i);

COMMIT 

或者你可以使用 sp_getapplock来序列化访问该代码。


由于两个会话生成了相同的发票号码,(UPDLOCK,HOLDLOCK)无法工作。 - FebWind
@FebWind - 如果您正在事务中运行它,就像我的答案一样,这是不可能的。HOLDLOCK始终锁定查询指定的整个范围,两个并发事务无法在同一资源上具有UPDLOCK - Martin Smith
@FebWind - 这在 SSMS 中很容易看到。只需打开两个窗口。并在两个窗口中执行以下操作 BEGIN TRANSACTION DECLARE @i INT SELECT @i = MAX(InvNumber) FROM Invoice WITH(UPDLOCK, HOLDLOCK) WHERE LocName = 'A' 第二个事务将被阻塞 "executing query" 等待第一个事务,直到您返回第一个窗口并执行 COMMITROLLBACK - Martin Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接