可序列化的事务死锁

时间:2014-12-07 20:57:03

标签: sql-server serialization transactions

文档说,serializable个事务一个接一个地执行。

但实际上似乎不是真理。这是两个几乎相等的交易,差异只是延迟15秒。

#1:

set transaction isolation level serializable
go
begin transaction
if not exists (select * from articles where title like 'qwe')
begin
waitfor delay '00:00:15'
insert into articles (title) values ('qwe')
end
commit transaction go

#2:

set transaction isolation level serializable
go
begin transaction
if not exists (select * from articles where title like 'qwe')
begin
insert into articles (title) values ('asd')
end
commit transaction go

自第一个事件开始以来,第二个事务已在几秒钟后运行。

结果是死锁。第一笔交易以

结束
Transaction (Process ID 58) was deadlocked on 
lock resources with another process and has been chosen as the deadlock victim. 
Rerun the transaction.

原因。

结论是,可序列化的交易不是串行的?

3 个答案:

答案 0 :(得分:5)

可序列化的交易不一定是串行执行的。

承诺只是事务只能在结果就像是按顺序执行(按任何顺序)时提交。

满足此保证的锁定要求经常会导致其中一个事务需要回滚的死锁。您需要编写自己的重试逻辑代码以重新提交失败的查询。

有关逻辑描述与实现之间差异的更多信息,请参阅The Serializable Isolation Level

答案 1 :(得分:4)

这里发生了什么: 因为事务1在可序列化的隔离级别中运行,所以它在等待时保持它在表项目上获得的共享锁定。这样,可以保证在事务终止之前,非存在条件保持为真。 事务2也获得共享锁,允许它执行存在的检查条件。然后,使用insert语句,事务2需要将共享锁转换为独占锁,但必须等待事务1持有共享锁。 当事务1完成等待时,它还请求转换为独占模式=>死锁的情况,1交易必须终止。

答案 2 :(得分:3)

我遇到了类似的问题,我发现:

来自MSDN

SERIALIZABLE 指定以下内容:

  • 语句无法读取已修改但尚未修改的数据 由其他交易承诺。
  • 没有其他事务可以修改已被读取的数据 直到当前交易完成的当前交易。
  • 其他事务无法插入具有键值的新行 落在当前任何语句读取的键范围内 交易,直到当前交易完成。

第二点并未说明两个会话都不能采用会导致死锁的共享锁。我们用SELECT提示解决了这个问题。

select * from articles WITH (UPDLOCK, ROWLOCK) where title like 'qwe'

如果在这种情况下它可以工作但没想过但我认为你必须锁定表部分,因为尚未创建行。