我有以下代码:
set transaction isolation level read committed; --this is for clarity only
DECLARE @jobName nvarchar(128);
BEGIN TRAN
SELECT @jobName = JobName
FROM dbo.JobDetails
WHERE ExecutionState_Status = 1
WAITFOR DELAY '00:00:10'
UPDATE dbo.JobDetails
SET ExecutionState_Status = 10
WHERE JobName = @jobName
COMMIT
第二件几乎相同:
set transaction isolation level read committed;
DECLARE @jobName nvarchar(128);
BEGIN TRAN
SELECT @jobName = JobName
FROM dbo.JobDetails
WHERE ExecutionState_Status = 1
WAITFOR DELAY '00:00:15'
UPDATE dbo.JobDetails
SET ExecutionState_Status = 20
WHERE JobName = @jobName
COMMIT
不同之处在于我们设置的状态(10 vs 20)和延迟(10s vs 15s)。
我在Management Studio中并行执行它们 - 两个标签。现在问题 - 读取已提交事务隔离级别按预期工作 - 应用最后一次修改并且两个脚本都成功执行。
然而,这不是我想要的 - 我只想执行一次而第二次不应该做任何事情。这就是我尝试将级别更改为REPEATABLE READ的原因。根据我的知识(我现在想要挑战),它的行为应该是这样的:
不幸的是,我看到的结果远非如此 - 事务处于死锁状态,其中一个被SQL Server杀死。我不太明白为什么会发生这种情况,因为他们以相同的顺序访问资源。
以下是测试所需的脚本:
CREATE TABLE [dbo].[JobDetails](
[JobName] [nvarchar](128) NOT NULL,
[ExecutionState_Status] [int] NULL DEFAULT ((0)),
CONSTRAINT [PK_dbo.JobDetails] PRIMARY KEY CLUSTERED
(
[JobName] ASC
))
GO
INSERT INTO JobDetails VALUES( 'My Job', 1)
UPDATE JobDetails SET ExecutionState_Status = 1
附加说明:
WHERE
答案 0 :(得分:2)
这个假设是错误的:
第二次交易同时开始,无法执行 选择,因为它被第一个锁定
两个repeatable read
次交易'select
获取并保持S
锁定密钥直到commit
。 S
锁是兼容的。当update
尝试获取与X
锁定不兼容的S
锁定时,它们会陷入僵局。
与此相反,select
交易中的read commited
即时发布S
锁定。{/ p>
使用exec sp_lock查看锁定,例如
DECLARE @jobName nvarchar(128);
BEGIN TRAN
SELECT @jobName = JobName
FROM dbo.JobDetails
WHERE ExecutionState_Status = 1
WAITFOR DELAY '00:00:10'
exec sp_lock 58,57
UPDATE dbo.JobDetails
SET ExecutionState_Status = 10
WHERE JobName = @jobName
COMMIT
答案 1 :(得分:1)
我得到link来回答这里发生的事情。这个例子几乎与我的相同,所以我不在这里复制它。
现在引用说明:
隔离级别可能发生第二种类型的死锁 如果您读取数据以更新数据,则可重复读取 后来。让我们看一下简单事务的T-SQL代码。
要导致此类死锁,您只需运行该事务即可 跨多个会话。您甚至不需要访问不同的数据 您可以从代码中看到范围。让我们试着解释一下会发生什么 这里。此事务跨多个会话运行时 同时,所有会话都可以获取共享锁以供阅读 数据。
因为您持有共享锁,直到交易结束 (COMMIT或ROLLBACK)在Repeatable Read中,以下更新 声明无法获取必要的更新锁,因为它们是 已被不同会话中获取的共享锁阻止。 死锁!
解决方案 - 将WITH (UPDLOCK)
添加到第一个选择:
SELECT @jobName = JobName
FROM dbo.JobDetails WITH (UPDLOCK)
WHERE ExecutionState_Status = 1
现在我需要考虑如何将该解决方案应用于ORM ..