为什么SQL Server死锁受害者错误(1205)会终止所有事务?

时间:2018-08-07 13:05:06

标签: sql sql-server tsql deadlock

请考虑以下两个表:

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被设置为on(在这种情况下不是),因为在导致死锁的调用之后没有执行任何其他查询语句。

该问题的原因是,是否发生死锁,是否知道是否可以再次运行查询。如果它发生在较大的事务中,并且打算在其中调用多个查询,则销毁该外部事务将意味着 不安全地重新运行受死锁影响的查询。但是,如果只停止其周围环境,那将是安全的。

2 个答案:

答案 0 :(得分:6)

SQL Server没有真正的嵌套事务。有SAVE TRANSACTION和保存点名称之类的功能,可能被迫像嵌套事务一样工作,但实际上不是 1

因此ROLLBACK的行为(不尝试使用保存点)始终会影响所有事务,无论嵌套级别如何。

并且死锁破坏者强制执行的回滚永远没有机会指定保存​​点名称。


1 值得注意的是,每个人都必须“开玩笑”。您不能将编写的现有代码使用BEGIN / ROLLBACK并将其嵌套在事务中。必须将其重写为SAVE TRANSACTION name / ROLLBACK name,现在它依赖于调用代码来始终将其包装在现有事务中

答案 1 :(得分:1)

  

为什么发生死锁时内部和外部事务都结束了?

您还有什么建议?编写可以解决所有可能情况的AI?快速失败是安全编程的已知原理。选择一个,杀死它,完成-让程序员修复他们创建的混乱。

也了解没有“内部交易”。只有一个交易-其他内部交易包含在外部交易中,因此必须回滚外部交易。