事务隔离级别REPEATABLE READ导致死锁

时间:2013-09-01 11:05:23

标签: sql-server transactions

在事务隔离级别REPEATABLE READ上打开连接后,我的应用程序的一部分按业务逻辑更新表。在极少数情况下,如果此操作与应用程序的另一部分重合,该部分打开不同的连接并尝试将同一记录重置为其默认值。我收到以下错误

Msg 1205, Level 13, State 45, Line 7
Transaction (Process ID 60) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

我认为我可以使用以下示例重新生成该问题。

1

create table Accounts
(
id int identity(1,1),
Name varchar(50),
Amount decimal
)

2

insert into Accounts (Name,Amount) values ('ABC',5000)
insert into Accounts (Name,Amount) values ('WXY',4000)
insert into Accounts (Name,Amount) values ('XYZ',4500)

3

以隔离级别启动长事务作为REPEATABLE READ

Set transaction isolation level REPEATABLE READ

begin tran

declare @var int

select @var=amount 
from Accounts
where id=1

waitfor delay '0:0:10'

if @var > 4000

update accounts 
set amount = amount -100;

Commit

4

虽然上面的Step.3仍在执行中。在另一个连接上启动另一个事务

Begin tran

update accounts
set Amount = 5000
where id = 1

commit tran

在步骤3中启动的事务最终会完成,但在步骤4中启动的事务将失败,并显示以下错误消息。

Msg 1205, Level 13, State 45, Line 7
Transaction (Process ID 60) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

我可以选择在步骤4中最终运行事务。我们的想法是能够将记录重置为默认值,并且在这种情况下应该覆盖在其他事务上执行的任何事务。如果两个事务都不是并发的,我没有看到任何问题。

3 个答案:

答案 0 :(得分:3)

  

我们的想法是能够将记录重置为默认值

您希望以何种顺序应用更新?你想要“重置”总是通过吗?然后,您需要在步骤3中的更新完成后严格执行重置。此外,重置更新应使用更高的锁定模式以避免死锁:

update accounts WITH (XLOCK)
set Amount = 5000
where id = 1

这样重置会等待另一个事务先完成,因为另一个事件有一个S锁。

或者,habe步骤3获得U锁或X锁。

答案 1 :(得分:2)

这是死锁图: enter image description here

<强>说明:

t = SPID 56(SELECT @var ... WHERE id=1)对id = 1的所有行采用S lock(共享)

t + 1 = SPID 59(UPDATE ... WHERE id=1)在id = 1的“第一”行上获取U lock(更新锁定)(此表是一个堆,并且没有行的顺序 - &gt;这就是那些“......”的原因。这是可能的,因为S和U锁是兼容的。然后它检查谓词(WHERE id=1),此评估的结果为TRUE,然后它尝试将U lock转换为X lock以更新当前行({{ 1}})但SET Amount = 5000X lock(SPID 56)不兼容。因此,SPID 59开始等待SPID 56释放S lock

t + 2 = SPID 56(S lock)和id {1的UPDATE ... SET Amount = Amount - 100我认为死锁图有错误,因为它显示SPID 56有{{1} }:请参阅S lock )并尝试将此U lock转换为Owner mode: U。但是SPID 59在此行上已经有S lock(id = 1)。由于两个U lock不兼容,因此会出现 nice 死锁。

因此对于第一行(id = 1,RowId = U lock; U lock(s)表示文件ID 1中的页面488中的插槽0; 7 = db id):

  • SPID 56在id = 1
  • 的行上占用"RID: 7:1:488:0"
  • SPID 59需要:0,然后尝试将其转换为S lock(它不能)并等待,
  • SPID 56尝试将U lock转换为X lock
  • 因为S lock are incompatible - &gt;死锁。

解决方案:您只需要U lock列上的索引(具有IDENTITY属性)。在这种情况下,我添加了一个CLUSTERED索引:

U locks

答案 2 :(得分:0)

您可以将setp 4中事务的死锁优先级设​​置得更高 有关详细信息,请参阅http://technet.microsoft.com/en-us/library/ms186736.aspx