【问题标题】:Does tablockx have to been specified in each session?是否必须在每个会话中指定 tablockx?
【发布时间】:2013-08-30 21:04:39
【问题描述】:

我分别在两个会话中有两个测试事务。假设这两个事务将同时运行。我试图做的是让一个交易在另一笔交易完成后正确插入发票号码。没有重复。我做了如下。但是如果我在会话 2 中使用 (tablockx) 删除,它们将不再起作用。我查了在线书,但没有答案。有人会帮忙吗? Serializable 不起作用,因为两个 SELECT 在这里彼此互斥。谢谢。

在会话 1 中:

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 

在会话 2 中:

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 

【问题讨论】:

    标签: sql-server sql-server-2008 tsql sql-server-2005 sql-server-2008-r2


    【解决方案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 - 如果您在事务中运行它作为我的回答,这是不可能的。 HOLDLOCK 始终至少锁定查询指定的整个范围,两个并发事务不可能在同一资源上拥有 UPDLOCK
    • @FebWind - 这在 SSMS 中很容易看到。只需打开两个窗口。并在BEGIN TRANSACTION DECLARE @i INT SELECT @i = MAX(InvNumber) FROM Invoice WITH(UPDLOCK, HOLDLOCK) WHERE LocName = 'A' 中执行以下操作,第二个事务将被阻止“执行查询”等待第一个,直到您返回第一个窗口并执行COMMITROLLBACK
    猜你喜欢
    • 2015-05-13
    • 1970-01-01
    • 2011-05-19
    • 2013-04-29
    • 1970-01-01
    • 2012-01-22
    • 2011-07-05
    • 1970-01-01
    • 2018-12-29
    相关资源
    最近更新 更多