大型过程中的SQL隔离级别或锁定

时间:2014-03-28 12:06:23

标签: tsql stored-procedures transactions

我有大型存储过程来处理用户操作。

它们由多个select语句组成。这些被过滤,大多数时候只获得一行。选择被复制到临时表或以其他方式评估。 最后,merge-Statement执行DB中所需的更改。 所有都封装在一个交易中。

我有来自用户的并发输入,并且应该锁定select语句的选定行以保持数据完整性。

如何锁定所有select语句的选定行,以便在当前事务处理过程中不通过其他事务更新?

ROWLOCK和HOLDLOCK的表提示组合是否以只锁定所选行的方式工作,或者由于HOLDLOCK而锁定整个表?

SELECT *
FROM dbo.Test    
WITH (ROWLOCK HOLDLOCK ) 
WHERE id = @testId

我可以改为使用

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
交易开始后

?或者这会锁定整个表格吗? 我正在使用SQL2008 R2,但如果SQL2012中的工作方式不同,也会感兴趣。

PS:我刚看过表提示UPDLOCK和SERIALIZE。 UPDLOCK似乎是一个只锁定一行的解决方案,似乎UPDLOCK总是锁定而不是ROWLOCK,它只指定锁是应用基于行的IF锁。我仍然对解决这个问题的最佳方法感到困惑......

1 个答案:

答案 0 :(得分:0)

更改隔离级别修复了问题(并锁定在行级别):

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

以下是我测试它的方式。 我在SQL Management Studio的空白页面中创建了一个语句:

begin tran
select 
    *
into #message
from dbo.MessageBody
where MessageBody.headerId = 28

WAITFOR DELAY '0:00:05'

update dbo.MessageBody set [message] = 'message1'
where headerId = (select headerId from #message)

select * from dbo.MessageBody where headerId = (select headerId from #message)

drop table #message
commit tran

执行此语句(由于延迟而在最后5秒内),我在另一个窗口中调用了第二个查询:

begin tran

select 
    *
into #message
from dbo.MessageBody
where MessageBody.headerId = 28

update dbo.MessageBody set [message] = 'message2'
where headerId = (select headerId from #message)

select * from dbo.MessageBody where headerId = (select headerId from #message)

drop table #message
commit tran

我很惊讶它立刻执行了。这是由于默认的SQL Server事务级别" Read Commited" http://technet.microsoft.com/en-us/library/ms173763.aspx。由于第一个脚本的更新是在延迟之后完成的,因此在第二个脚本期间还没有任何umcommited更改,因此读取并更新了第28行。

将隔离级别更改为Serialization阻止了这种情况,但它也阻止了并发性 - 两个scip都是连续执行的。

没关系,因为两个脚本都读取并更改了同一行(通过headerId = 28)。将headerId更改为第二个脚本中的另一个值,这些语句是并行执行的。所以来自SERIALIZATION的锁似乎在行级别上。

添加表提示

WITH ( SERIALIZABLE)

在第一个语句的第一个选择中也会阻止对所选行的进一步读取。