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

Posted

技术标签:

【中文标题】大型过程中的 SQL 隔离级别或锁【英文标题】:SQL Isolation levels or locks in large procedures 【发布时间】:2014-03-28 12:06:22 【问题描述】:

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

它们由多个选择语句组成。这些被过滤,大多数时候只得到一行。选择被复制到临时表中或以其他方式评估。 最后,合并语句在数据库中进行所需的更改。 一切都封装在一个事务中。

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

如何锁定所有选择语句的选定行,以便在当前事务正在进行时不会通过其他事务更新它们?

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 锁。我仍然对解决这个问题的最佳方法感到困惑......

【问题讨论】:

您有几个问题都包含在文档中。你为什么认为 HOLDLOCK 会改变锁? 我知道 holdlock 强制锁定,并希望结合 rowlock 只锁定单行。我澄清了问题,我可以删除问题后的所有内容,但也许有助于理解问题。 【参考方案1】:

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

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 造成的。由于第一个脚本的更新是在延迟之后完成的,因此在第二个脚本期间还没有未提交的更改,因此读取并更新了第 28 行。

将 Isolation 级别更改为 Serialization 可以防止这种情况发生,但也可以防止并发 - 两个 scipts 都是连续执行的。

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

添加表格提示

WITH ( SERIALIZABLE)

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

【讨论】:

以上是关于大型过程中的 SQL 隔离级别或锁的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因

mysql,oracle,sql server中的默认事务隔离级别查看,更改

mysql,oracle,sql server中的默认事务隔离级别查看,更改

SQL 隔离级别

简述mysql的事务隔离级别都有哪些

SQL Server 事务隔离级别详解