我正在编写一个程序,用于协调实时数据库上的有关交易。我正在做的工作不能作为一个集合操作完成,所以我使用两个嵌套游标。
我需要在每个客户端协调时对事务表进行独占锁定,但我想释放锁定,让其他人在我处理的每个客户端之间运行查询。
我希望在行级别而不是表级别上执行独占锁定,但what I have read so far表示如果其他事务在with (XLOCK, ROWLOCK, HOLDLOCK)
隔离运行,则无法执行READCOMMITED
等级(对我而言)。
我是否正确地使用了表级别的独占锁,并且在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
答案 0 :(得分:10)
我无法相信XLOCK
不会阻止read committed
的并发读者,所以我只是转载它:这是真的。脚本:
第1节:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRAN
SELECT * FROM T WITH (ROWLOCK, XLOCK, HOLDLOCK /*PAGLOCK, TABLOCKX*/) WHERE ID = 123
第二节:
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锁定,而另一个事务则可以快速读取聚簇索引PK_T
。
因此,即使存在独占锁定,事务也可以独立执行,这是非常正常的。
答案 1 :(得分:3)
如果您只是担心其他读者,那么您不应该需要排他锁,模式
Begin Transaction
Make Data Inconsistent
Make Data Consistent
Commit Transaction
应该没问题。唯一会看到不一致数据的会话是那些使用nolock
或Read Uncommitted
的会话,或那些希望在不使用Repeatable Rows
或Serializable
的情况下进行多次一致性读取的会话。
在回答这个问题时,在我看来,采取独家锁定的正确方法是安排事情,以便引擎为你做。