执行删除和插入语句时子表发生死锁

问题描述 投票:0回答:2

我在下面这两个表上执行删除,然后执行插入,但遇到间歇性死锁。

Schedule.Assignments(父表)

1

Schedule.Schedules(子表):

2

尽管正在

schedule.Schedules

 表(父表)上执行操作,但 
schedule.Assignments
 表(子表)上间歇性地发生两种类型的死锁。两者都有相同的死锁图,如下所示。

  1. schedule.Assignments

    表上的Insert和Delete语句之间出现死锁。

  2. schedule.Assignments

    表上的相同删除语句之间出现死锁。

[死锁图]

3

Deadlock Graph1 : https://pastebin.com/raw/ZpQUrjBV Deadlock Graph2 : https://pastebin.com/raw/DhnuyZ7a
包含插入和删除语句的存储过程:

https://pastebin.com/raw/6DNh2RxH

查询执行计划:

PasteThePlan

[编辑]

作业架构:

作业架构

作业索引:

作业索引

计划架构:

计划架构

时间表索引:

时间表索引

我无法理解为什么死锁对象显示为子表,而死锁涉及的进程显示父表上的插入/删除。

请分享您对如何解决这些僵局的想法?

sql sql-server database deadlock
2个回答
3
投票
看起来你的死锁是由

Schedules

 上的大表扫描引起的。扫描发生在手术过程中的三个不同位置。相反,应该发生的是 
Nested Loops
 上的简单 
Index Seek
/
ParentId

您进行扫描的原因是因为

ParentId

 上的连接条件位于 
nvarchar(50)
 列和 
bigint
 之间。我建议您通过将 
ParentId
 设为 
bigint
 来解决此问题。

ALTER TABLE schedule.Schedules ALTER COLUMN ParentId bigint NULL;
执行此操作时,您可能需要删除并重新创建索引或约束。


顺便说一句,虽然您似乎在

schedule.Assignments (OldResourceRequestId)

 上有一个索引,但它并不是唯一的。这会导致各个子查询出现 
Assert
,以确保只返回一行,并且还可能影响查询统计/估计。

我建议您将其更改为唯一索引(如果可能)。如果存在重复项,那么您无论如何都需要重新考虑这些连接,因为您会得到重复的结果或失败

Assert

CREATE NONCLUSTERED INDEX [IX_Assignments_OldResourceRequestId] ON schedule.Assignments ( OldResourceRequestId ASC ) WITH (DROP_EXISTING = ON, ONLINE = ON) ON PRIMARY;


您还应该记下您的

IF

 陈述。它们没有缩进,并且不清楚实际发生的情况是,由于缺少 
BEGIN
 
END
,只有后面的第一个语句是有条件的。正如另一个答案中提到的,无论如何,
IF
可能都没有必要。


1
投票
没有足够的信息来确定,但我会告诉你我从哪里开始。

    消除 IF EXISTS 测试。直接进入删除即可。如果该值不存在,则 DELETE 无论如何都会很快。您也不处于事务中,这使您可以在 SELECT 和 DELETE 之间更改表。
  1. 使用 WHERE EXISTS 子查询将专有的 DELETE...JOIN 重写为 ANSI DELETE。专有版本存在一些问题,目前我还不清楚其详细信息。最好以标准的方式编写,不要引发问题。
你说“子”表和“父”表。时间表是否有已定义的分配外键?应该是的。

我不确定这些改变是否会消除问题,但在我看来,它们会降低这种可能性。您可以通过减少语句数量来增加原子性,并通过消除分支来强制过程的每次调用执行完全相同的逻辑序列。

© www.soinside.com 2019 - 2024. All rights reserved.