这更像是一个 TABLOCK 对我不起作用而 TABLOCKX 对我起作用的例子。
我有 2 个会话,它们都使用默认(已提交)隔离级别:
会话 1 是一个显式事务,它将数据从链接服务器复制到数据库中的一组表中,运行需要几秒钟。 [例如,它会删除问题]
Session 2 是一个插入语句,它只是将行插入到 Session 1 未对其进行更改的表中。 [例如,它插入答案]。
(实际上有多个会话同时向表中插入多条记录,而会话 1 正在运行其事务)。
会话 1 必须查询会话 2 插入的表,因为它无法删除依赖于会话 2 添加的条目的记录。[示例:删除尚未回答的问题]。
因此,当 Session 1 正在执行并且 Session 2 尝试插入时,Session 2 每次都在死锁中失败。
因此,会话 1 中的删除语句可能如下所示:
从 tblQ 中删除 tblA 左连接 tblX on ...
左加入 tblA a ON tblQ.Qid = tblA.Qid
WHERE ... a.QId 为 NULL 并且 ...
死锁似乎是由于在 Session 2, [3, 4, 5, ..., n] 尝试插入 tblA 时查询 tblA 之间的争用造成的。
就我而言,我可以将 Session 1 的事务的隔离级别更改为 SERIALIZABLE。当我这样做时:事务管理器已禁用对远程/网络事务的支持。
所以,我可以按照此处接受的答案中的说明来解决它:The transaction manager has disabled its support for remote/network transactions
但是a)我首先对将隔离级别更改为SERIALIZABLE 感到不舒服-据说它会降低性能并且可能会产生我没有考虑过的其他后果,b)不明白为什么这样做会突然导致事务在链接的服务器之间工作时出现问题,并且 c) 不知道通过启用网络访问可能会打开哪些漏洞。
在一个非常大的事务中似乎只有 6 个查询导致了问题。
所以,我读到了 TABLOCK 和 TabLOCKX。
我对这些差异不是很清楚,也不知道两者是否可行。但它似乎会。首先我尝试了 TABLOCK,它似乎没有任何区别。竞争会话产生相同的死锁。然后我尝试了TABLOCKX,没有更多的死锁。
所以,在六个地方,我只需要添加一个 WITH (TABLOCKX)。
因此,会话 1 中的删除语句可能如下所示:
从 tblQ 中删除 tblA q LEFT JOIN tblX x on ...
LEFT JOIN tblA a WITH (TABLOCKX) ON tblQ.Qid = tblA.Qid
WHERE ... a.QId 为 NULL 并且 ...