正确的方式采取排他锁

我正在编写一个程序,该程序将协调实时数据库上的finical交易。 我正在做的工作不能作为集合操作完成,所以我使用了两个嵌套游标。

当我协调每个客户端时,我需要在事务处理表上独占一个锁,但是我想释放锁并让其他人在我处理的每个客户端之间运行他们的查询。

我希望在行级而不是表级别独占一个锁,但是我迄今为止读过的内容说如果其他事务在READCOMMITED隔离级别运行with (XLOCK, ROWLOCK, HOLDLOCK)我不能with (XLOCK, ROWLOCK, HOLDLOCK)这是给我的)。

我是否正确地进行了表级排他锁,并且在Server 2008 R2中有没有办法使行级排他锁以我想要的方式工作,而无需修改数据库上运行的其他查询?

declare client_cursor cursor local forward_only for 
     select distinct CLIENT_GUID from trnHistory
open client_cursor

declare @ClientGuid uniqueidentifier
declare @TransGuid uniqueidentifier

fetch next from client_cursor into @ClientGuid
WHILE (@@FETCH_STATUS <> -1)
BEGIN
    IF (@@FETCH_STATUS <> -2)
    BEGIN
        begin tran

        declare @temp int

        --The following row will not work if the other connections are running READCOMMITED isolation level
        --select @temp = 1 
    --from trnHistory with (XLOCK, ROWLOCK, HOLDLOCK) 
    --left join trnCB with (XLOCK, ROWLOCK, HOLDLOCK) on trnHistory.TRANS_GUID = trnCB.TRANS_GUID
    --left join trnClients with (XLOCK, ROWLOCK, HOLDLOCK) on trnHistory.TRANS_GUID = trnClients.TRANS_GUID
    --(Snip) --Other tables that will be "touched" during the reconcile
    --where trnHistory.CLIENT_GUID = @ClientGuid

        --Works allways but locks whole table.
    select top 1 @temp = 1 from trnHistory with (XLOCK, TABLOCK) 
    select top 1 @temp = 1 from trnCB with (XLOCK, TABLOCK)
    select top 1 @temp = 1 from trnClients with (XLOCK, TABLOCK)
    --(Snip) --Other tables that will be "touched" during the reconcile

        declare trans_cursor cursor local forward_only for 
                select TRANS_GUID from trnHistory where CLIENT_GUID = @ClientGuid order by TRANS_NUMBER
        open trans_cursor

        fetch next from trans_cursor into @TransGuid
        WHILE (@@FETCH_STATUS <> -1)
        BEGIN
            IF (@@FETCH_STATUS <> -2)
            BEGIN

                --Do Work here

            END
            fetch next from trans_cursor into @TransGuid
        END

        close trans_cursor
        deallocate trans_cursor

            --commit the transaction and release the lock, this allows other 
            -- connections to get a few queries in while it is safe to read.
        commit tran
    END

    fetch next from client_cursor into @ClientGuid
END 

close client_cursor
deallocate client_cursor

如果你只是担心其他读者,那么你不应该需要排他锁,这种模式

Begin Transaction

  Make Data Inconsistent

  Make Data Consistent

Commit Transaction

应该没事。 唯一会看到不一致数据的会话是使用nolockRead Uncommitted那些会话,或那些希望在不使用可Repeatable Rows或可Serializable情况下进行多个一致读取的会话。

在回答这个问题时,在我看来,采取独占锁定的正确方法是安排事情,让引擎为你做。


我无法相信一个XLOCK不会在read committed阻止并发读者,所以我只是重复了它:这是真的。 脚本:

第一部分:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRAN

SELECT * FROM T WITH (ROWLOCK, XLOCK, HOLDLOCK /*PAGLOCK, TABLOCKX*/) WHERE ID = 123

会议2:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRAN

SELECT * FROM T WHERE ID = 123

插入你手边的一些表名。 会话2没有被阻止。

我也试过使用PAGLOCK但那也没有效果。 接下来我尝试了一个TABLOCKX但那也不起作用!

所以基于表锁的策略不起作用。 我认为你必须修改读者,以便他们

  • 使用快照隔离来获得一致的视图(在写入之前)
  • 使用较高的隔离级别可被作者阻止
  • 当然,有一个令人讨厌的解决方法来真正地锁定表:改变它的模式。 这将需要一个Sch-M锁,与基本上任何访问表相冲突。 它甚至包含一些元数据读取操作。 它可能看起来像这样:

    --just change *any* setting in an idempotent way
    ALTER TABLE T SET (LOCK_ESCALATION = AUTO)
    

    我测试了这个工作。


    SQL Server是否正确不遵守XLOCK ? 或者这是产品中的缺陷? 我认为它是正确的,因为它符合READ COMMITTED的记录属性。 另外,即使使用SERIALIZABLE也有一种情况是一个事务可以专门锁定一行,而另一个可以读取同一行! 这可能在索引存在的情况下发生。 一个事务可能对非聚簇索引IX_T_SomeCol进行X锁定,而另一个事务则可以IX_T_SomeCol读取聚簇索引PK_T

    因此,即使在排他锁定的情况下,事务也可以独立执行是非常正常的事情。

    链接地址: http://www.djcxy.com/p/32869.html

    上一篇: Correct way to take a exclusive lock

    下一篇: DeadLock SQL server withTransactionScope ReadCommited