可序列化的事务死锁

Wil*_*Dev 4 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
Run Code Online (Sandbox Code Playgroud)

#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
Run Code Online (Sandbox Code Playgroud)

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

结果是死锁.第一笔交易死了

Transaction (Process ID 58) was deadlocked on 
lock resources with another process and has been chosen as the deadlock victim. 
Rerun the transaction.
Run Code Online (Sandbox Code Playgroud)

原因.

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

小智 8

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


Mar*_*ith 6

可序列化的事务不一定是串行执行的.

承诺只是如果结果就像它们已经连续执行(以任何顺序),那么事务只能提交.

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

有关逻辑描述和实现之间差异的更多信息,请参阅可序列化隔离级别.


nur*_*uro 5

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

来自MSDN

SERIALIZABLE 指定以下内容:

  • 语句无法读取已被其他事务修改但尚未提交的数据。
  • 在当前事务完成之前,任何其他事务都不能修改当前事务已读取的数据。
  • 在当前事务完成之前,其他事务无法插入其键值落在当前事务中任何语句读取的键范围内的新行。

第二点并没有说明两个会话都不能获取将导致死锁的共享锁。我们通过 SELECT 的提示解决了这个问题。

select * from articles WITH (UPDLOCK, ROWLOCK) where title like 'qwe'
Run Code Online (Sandbox Code Playgroud)

还没有尝试在这种情况下是否有效,但我认为您必须锁定表部分,因为尚未创建该行。