在事务隔离级别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中最终运行事务。我们的想法是能够将记录重置为默认值,并且在这种情况下应该覆盖在其他事务上执行的任何事务。如果两个事务都不是并发的,我没有看到任何问题。
答案 0 :(得分:3)
我们的想法是能够将记录重置为默认值
您希望以何种顺序应用更新?你想要“重置”总是通过吗?然后,您需要在步骤3中的更新完成后严格执行重置。此外,重置更新应使用更高的锁定模式以避免死锁:
update accounts WITH (XLOCK)
set Amount = 5000
where id = 1
这样重置会等待另一个事务先完成,因为另一个事件有一个S锁。
或者,habe步骤3获得U锁或X锁。
答案 1 :(得分:2)
这是死锁图:
<强>说明:强>
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 = 5000
和X 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):
"RID: 7:1:488:0"
:0
,然后尝试将其转换为S lock
(它不能)并等待,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