考虑以下两个表:
create table testDeadlockTable1 (
c1 int not null,
c2 int not null
)
create table testDeadlockTable2 (
c1 int not null,
c2 int not null
)
有了这些数据:
insert testDeadlockTable1 values (1, 1)
insert testDeadlockTable1 values (2, 2)
insert testDeadlockTable1 values (3, 3)
insert testDeadlockTable1 values (4, 4)
insert testDeadlockTable2 values (1, 1)
insert testDeadlockTable2 values (2, 2)
insert testDeadlockTable2 values (3, 3)
insert testDeadlockTable2 values (4, 4)
考虑以下两个存储过程:
create proc testDeadlockTestA
as
begin tran
update testDeadlockTable1
set c1 = 3
where c2 = 1
waitfor delay '00:00:01' -- sleep 1 second
select c1
from testDeadlockTable2
where c2 = 3
commit tran
go
create proc testDeadlockTestB
as
begin tran
update testDeadlockTable2
set c1 = 5
where c2 = 2
waitfor delay '00:00:01' -- sleep 1 second
select c1
from testDeadlockTable1
where c2 = 4
commit tran
go
在一个查询会话中,先调用
testDeadlockTestA
,再立即在另一个会话中调用testDeadlockTestB
。后者会被选择为死锁牺牲品。两个会话中,
@@trancount
在结束时都是0。现在,在每个查询会话中都使用
begin tran
开始,这样当存储过程被调用时,@@trancount
是1。因此,死锁应该发生在每个存储过程开始的事务内部(也就是内部交易)。会话A未被选择为死锁牺牲品,其
@@trancount
为1,正如我们所期望的那样(我们没有结束外部事务)。但是,受害者会话B的@@trancount
为0。为什么死锁发生时内部和外部事务都结束了?是否有办法确保只在死锁情况下结束内部事务?
似乎死锁错误的行为就像
XACT_ABORT
被设置为开启一样(在这种情况下没有开启),因为在导致死锁的调用之后,任何其他查询语句都不会被执行。这个问题的原因是想知道是否可以重新运行查询,如果发生死锁。如果它发生在一个更大的事务中,该事务旨在调用几个查询,则销毁外部事务意味着重新运行被死锁牺牲的查询是不安全的。但如果它只停止其直接环境,则是安全的。