我们在 SQL Server 中遇到“死锁”问题,其中不涉及显式锁,并且想知道如何绕过它们。
一些相关的背景信息:我们的应用程序非常古老且庞大。我们最近开始消除一些阻碍并发的问题,因此我们踏上了 SQL Server 上的死锁问题。我们没有资源来处理应用程序中的每个 select 语句,但正在寻找配置级别的更通用方法。
我们可以将一个示例性问题简化如下:基本上,我们有两个实体,EntityA
并且EntityB
。两者都映射到 SQL Server 架构中的各个表。在这两个实体之间,存在 am:n 关系,通过一个AToB
表在数据库中映射,该表包含一些额外的上下文(即,AToB
对于相同的A
和B
.
在一个业务操作期间,一个新的实例A
和B
被插入到数据库以及表中的多个条目AToB
中。稍后在同一事务中再次读取所有这些数据(没有 for update
)。在并行执行此操作时会发生死锁。这些死锁链接到AToB
表。
比如说,我们有A1
and B1
,它们通过A1B1_1
andA1B1_2
和A2
and链接B2
,它们通过A2B2_1
and链接A2B2_2
。我的猜测是会发生以下情况:
t1 -> INSERT A1
t1 -> INSERT B1
t1 -> INSERT A1B1_1 (PAGE1)
t2 -> INSERT A2
t2 -> INSERT B2
t2 -> INSERT A2B2_1 (PAGE1)
t1 -> INSERT A1B1_2 (PAGE2)
t2 -> INSERT A2B2_2 (PAGE2)
t1 -> SELECT * FROM AToB WHERE AToB.A=A1
t1 -> SELECT * FROM AToB WHERE AToB.A=A2
AToB
现在,在对表的并发读取期间,t1
获得了一个锁PAGE1
并t2
获得了一个锁,PAGE2
导致死锁。
第一个问题:这是发生僵局的合理解释吗?
第二个问题:在研究过程中给我的印象是,索引AToB.A
可能会迫使 SQL Server 锁定表上的较少条目(甚至可能将其减少为一行,而不是页锁)。这是正确的吗?
第三个问题:我进一步的印象,这个问题可以通过snapshot-locking来解决。那正确吗?
我们尝试了这种方法,但是它把我们带入了下一个地狱循环:在业务事务期间,有一次业务标识符被分配给A
. 这来自一个单独的表,它在A
s 中必须是唯一的。不可能通过数据库序列来分配它。我们的解决方案是通过第四个表上的select
/来分配这个标识符。这是通过语句完成的。当采用快照锁定时,此锁定在获取期间被忽略,并且只会在提交期间导致乐观锁定异常。这导致我们update
Identifier
for update
for update
第四个问题:当使用快照锁定时,是否仍然有特殊事务仍然运行在悲观锁定上,或者是否可以告诉 SQL Server,某些表被排除在乐观锁定之外?