为什么同一个表上两个ID不同的Delete会导致死锁

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

我的 MS SQL Server 2019 数据库中面临源自两个删除语句的死锁。我们的隔离级别是 READ_COMMITTED,但未打开 READ_COMMITTED_SNAPSHOT。

我的猜测是,发生死锁是因为 ID 非常接近,因此驻留在同一页面中。但根据死锁图,两个进程ID访问不同的页面。

为什么会发生这种死锁以及如何解决它?

我的一个想法是强制执行删除的系统为每个删除使用相同的连接/会话,这将导致它们按顺序执行而不是同时执行。但这会增加我们的运行时间。

根据要求,我在下面分享了所涉及的查询、完整的表和索引定义、死锁 XDL/XML 图作为文本,并请通过 brentozar.com/pastetheplan 分享查询计划。

出于安全原因,我已将表名称替换为“Our_Table”,并将过滤后的列名称替换为“Foreign_Key_Column”。在创建表语句中,我以类似的方式替换列和键名称,但尝试保持有效性。

注意:服务器语言是德语。

第一:涉及的查询

Delete from Our_Table where Foreign_Key_Column = 78905 --Process 51
Delete from Our_Table where Foreign_Key_Column = 78906 --Process 68

第二:完整的表和索引定义

USE [Our_Database]
GO

/****** Object:  Table [dbo].[Our_Table]    Script Date: 06.02.2024 16:17:28 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Our_Table](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Other_Foreign_Key_Column_1] [int] NOT NULL,
    [Foreign_Key_Column] [int] NULL,
    [Other_Foreign_Key_Column_2] [int] NULL,
    [Column_1] [date] NOT NULL,
    [Column_2] [decimal](19, 3) NOT NULL,
 CONSTRAINT [Primary_Key_Name] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Our_Table]  WITH CHECK ADD  CONSTRAINT [Constraing_Name_1] FOREIGN KEY([Other_Foreign_Key_Column_1])
REFERENCES [dbo].[Other_Table_1] ([Id])
GO

ALTER TABLE [dbo].[Our_Table] CHECK CONSTRAINT [Constraing_Name_1]
GO

ALTER TABLE [dbo].[Our_Table]  WITH CHECK ADD  CONSTRAINT [Constraing_Name_2] FOREIGN KEY([Other_Foreign_Key_Column_2])
REFERENCES [dbo].[Other_Table_2] ([Id])
GO

ALTER TABLE [dbo].[Our_Table] CHECK CONSTRAINT [Constraing_Name_2]
GO

ALTER TABLE [dbo].[Our_Table]  WITH CHECK ADD  CONSTRAINT [Constraing_Name_3] FOREIGN KEY([Foreign_Key_Column])
REFERENCES [dbo].[Other_Table_3] ([Id])
GO

ALTER TABLE [dbo].[Our_Table] CHECK CONSTRAINT [Constraing_Name_3]
GO

第一个索引:

USE [Our_Database]
GO

/****** Object:  Index [Index_Name_1]    Script Date: 06.02.2024 16:24:14 ******/
CREATE NONCLUSTERED INDEX [Index_Name_1] ON [dbo].[Our_Table]
(
    [Foreign_Key_Column] ASC
)
INCLUDE([Id],[Other_Foreign_Key_Column_1],[Column_1],[Other_Foreign_Key_Column_2],[Column_2]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

第二个索引:

USE [Our_Database]
GO

/****** Object:  Index [Index_Name_2]    Script Date: 06.02.2024 16:27:00 ******/
CREATE NONCLUSTERED INDEX [Index_Name_2] ON [dbo].[Our_Table]
(
    [Other_Foreign_Key_Column_1] ASC,
    [Foreign_Key_Column] ASC,
    [Column_1] ASC
)
INCLUDE([Id],[Other_Foreign_Key_Column_2],[Column_2]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

第三:死锁 XDL/XML 图作为文本

xml_report  <deadlock>   <victim-list>    <victimProcess id="process1eb2b5a3c28"/>   </victim-list>   <process-list>    <process id="process1eb2b5a3c28" taskpriority="0" logused="0" waitresource="PAGE: 8:1:12279678 " waittime="7830" ownerId="60983391" transactionname="DELETE" lasttranstarted="2023-11-30T18:20:47.690" XDES="0x1ebf3b43aa0" lockMode="U" schedulerid="6" kpid="8460" status="suspended" spid="51" sbid="0" ecid="4" priority="0" trancount="0" lastbatchstarted="2023-11-30T18:20:47.680" lastbatchcompleted="2023-11-30T18:20:47.680" lastattention="1900-01-01T00:00:00.680" clientapp=".Net SqlClient Data Provider" hostname="Our_Hostname" hostpid="8724" isolationlevel="read committed (2)" xactid="60983391" currentdb="8" currentdbname="Our_Database" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">     <executionStack>      <frame procname="adhoc" line="1" stmtend="152" sqlhandle="0x02000000cff49033589c5bbc920f9e3e6c20dd52ec7b2a6b0000000000000000000000000000000000000000">  unknown    </frame>      <frame procname="adhoc" line="1" stmtend="152" sqlhandle="0x0200000054ea521e213370ce6f8ed8419b18c5e68fe33f980000000000000000000000000000000000000000">  unknown    </frame>     </executionStack>     <inputbuf>  delete from Our_Table where Foreign_Key_Column = 78905   </inputbuf>    </process>    <process id="process1eb2b5b3c28" taskpriority="0" logused="0" waitresource="PAGE: 8:1:12280105 " waittime="7827" ownerId="60983390" transactionname="DELETE" lasttranstarted="2023-11-30T18:20:47.690" XDES="0x1eb3c43cd70" lockMode="U" schedulerid="8" kpid="11956" status="suspended" spid="68" sbid="0" ecid="6" priority="0" trancount="0" lastbatchstarted="2023-11-30T18:20:47.680" lastbatchcompleted="2023-11-30T18:20:47.680" lastattention="1900-01-01T00:00:00.680" clientapp=".Net SqlClient Data Provider" hostname="Our_Hostname" hostpid="8724" isolationlevel="read committed (2)" xactid="60983390" currentdb="8" currentdbname="Our_Database" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">     <executionStack>      <frame procname="adhoc" line="1" stmtend="152" sqlhandle="0x02000000cff49033589c5bbc920f9e3e6c20dd52ec7b2a6b0000000000000000000000000000000000000000">  unknown    </frame>      <frame procname="adhoc" line="1" stmtend="152" sqlhandle="0x02000000ac70fc271ab95a0c0039641710cac984cc29aae00000000000000000000000000000000000000000">  unknown    </frame>     </executionStack>     <inputbuf>  delete from Our_Table where Foreign_Key_Column = 78906   </inputbuf>    </process>    <process id="process1eb2b5c3c28" taskpriority="0" logused="0" waitresource="PAGE: 8:1:12280105 " waittime="7817" ownerId="60983390" transactionname="DELETE" lasttranstarted="2023-11-30T18:20:47.690" XDES="0x1f1c449d050" lockMode="U" schedulerid="10" kpid="2404" status="suspended" spid="68" sbid="0" ecid="4" priority="0" trancount="0" lastbatchstarted="2023-11-30T18:20:47.680" lastbatchcompleted="2023-11-30T18:20:47.680" lastattention="1900-01-01T00:00:00.680" clientapp=".Net SqlClient Data Provider" hostname="Our_Hostname" hostpid="8724" isolationlevel="read committed (2)" xactid="60983390" currentdb="8" currentdbname="Our_Database" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">     <executionStack>      <frame procname="adhoc" line="1" stmtend="152" sqlhandle="0x02000000cff49033589c5bbc920f9e3e6c20dd52ec7b2a6b0000000000000000000000000000000000000000">  unknown    </frame>      <frame procname="adhoc" line="1" stmtend="152" sqlhandle="0x02000000ac70fc271ab95a0c0039641710cac984cc29aae00000000000000000000000000000000000000000">  unknown    </frame>     </executionStack>     <inputbuf>  delete from Our_Table where Foreign_Key_Column = 78906   </inputbuf>    </process>    <process id="process1eb2b051848" taskpriority="0" logused="0" waitresource="PAGE: 8:1:12279678 " waittime="7815" ownerId="60983391" transactionname="DELETE" lasttranstarted="2023-11-30T18:20:47.690" XDES="0x1eb3c87baa0" lockMode="U" schedulerid="1" kpid="2576" status="suspended" spid="51" sbid="0" ecid="2" priority="0" trancount="0" lastbatchstarted="2023-11-30T18:20:47.680" lastbatchcompleted="2023-11-30T18:20:47.680" lastattention="1900-01-01T00:00:00.680" clientapp=".Net SqlClient Data Provider" hostname="Our_Hostname" hostpid="8724" isolationlevel="read committed (2)" xactid="60983391" currentdb="8" currentdbname="Our_Database" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">     <executionStack>      <frame procname="adhoc" line="1" stmtend="152" sqlhandle="0x02000000cff49033589c5bbc920f9e3e6c20dd52ec7b2a6b0000000000000000000000000000000000000000">  unknown    </frame>      <frame procname="adhoc" line="1" stmtend="152" sqlhandle="0x0200000054ea521e213370ce6f8ed8419b18c5e68fe33f980000000000000000000000000000000000000000">  unknown    </frame>     </executionStack>     <inputbuf>  delete from Our_Table where Foreign_Key_Column = 78905   </inputbuf>    </process>    <process id="process1edc2d288c8" taskpriority="0" logused="10000" waittime="2963" schedulerid="4" kpid="11376" status="suspended" spid="51" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2023-11-30T18:20:47.680" lastbatchcompleted="2023-11-30T18:20:47.680" lastattention="1900-01-01T00:00:00.680" clientapp=".Net SqlClient Data Provider" hostname="Our_Hostname" hostpid="8724" loginname="Console" isolationlevel="read committed (2)" xactid="60983391" currentdb="8" currentdbname="Our_Database" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">     <executionStack>      <frame procname="adhoc" line="1" stmtend="152" sqlhandle="0x02000000cff49033589c5bbc920f9e3e6c20dd52ec7b2a6b0000000000000000000000000000000000000000">  unknown    </frame>      <frame procname="adhoc" line="1" stmtend="152" sqlhandle="0x0200000054ea521e213370ce6f8ed8419b18c5e68fe33f980000000000000000000000000000000000000000">  unknown    </frame>     </executionStack>     <inputbuf>  delete from Our_Table where Foreign_Key_Column = 78905   </inputbuf>    </process>   </process-list>   <resource-list>    <pagelock fileid="1" pageid="12279678" dbid="8" subresource="FULL" objectname="Our_Database.dbo.Our_Table" id="lock1ecc8c77a80" mode="U" associatedObjectId="72057661130997760">     <owner-list>      <owner id="process1eb2b5c3c28" mode="U"/>     </owner-list>     <waiter-list>      <waiter id="process1eb2b5a3c28" mode="U" requestType="wait"/>     </waiter-list>    </pagelock>    <pagelock fileid="1" pageid="12280105" dbid="8" subresource="FULL" objectname="Our_Database.dbo.Our_Table" id="lock1ecc8d4f800" mode="U" associatedObjectId="72057661130997760">     <owner-list>      <owner id="process1edc2d288c8" mode="U"/>     </owner-list>     <waiter-list>      <waiter id="process1eb2b5b3c28" mode="U" requestType="wait"/>     </waiter-list>    </pagelock>    <pagelock fileid="1" pageid="12280105" dbid="8" subresource="FULL" objectname="Our_Database.dbo.Our_Table" id="lock1ecc8d4f800" mode="U" associatedObjectId="72057661130997760">     <owner-list>      <owner id="process1eb2b5b3c28" mode="U" requestType="wait"/>     </owner-list>     <waiter-list>      <waiter id="process1eb2b5c3c28" mode="U" requestType="wait"/>     </waiter-list>    </pagelock>    <pagelock fileid="1" pageid="12279678" dbid="8" subresource="FULL" objectname="Our_Database.dbo.Our_Table" id="lock1ecc8c77a80" mode="U" associatedObjectId="72057661130997760">     <owner-list>      <owner id="process1eb2b5a3c28" mode="U" requestType="wait"/>     </owner-list>     <waiter-list>      <waiter id="process1eb2b051848" mode="U" requestType="wait"/>     </waiter-list>    </pagelock>    <exchangeEvent id="Pipe1ebc82f6380" WaitType="e_waitPipeGetRow" waiterType="Coordinator" nodeId="7" tid="0" ownerActivity="sentData" waiterActivity="needMoreData" merging="false" spilling="false" waitingToClose="false">     <owner-list>      <owner id="process1eb2b051848"/>      <owner id="process1eb2b5a3c28"/>     </owner-list>     <waiter-list>      <waiter id="process1edc2d288c8"/>     </waiter-list>    </exchangeEvent>   </resource-list>  </deadlock>  

第四:查询计划

https://www.brentozar.com/pastetheplan/?id=BkiqrCJoT

sql-server t-sql deadlock
1个回答
0
投票

您的问题始于

Other_Table_4
Our_Table
上有一个外键。因此,每当
DELETE
上发生
Our_Table
时,服务器需要检查
Other_Table_4
中是否没有相关行,否则删除失败。

但这里的问题是

Other_Table_4
没有正确的索引,因此它对
Other_Table_4
进行大量扫描以查找行。然后,这会导致其他执行相同操作的查询陷入死锁。

如果表上有外键,那么子表中的该列必须有索引,否则最终会进行巨大的表扫描来查找这些行。

CREATE INDEX FKOur_Table ON Other_Table_4 (FKOur_Table)

您可以将其他列添加到键或作为

INCLUDE
,但该列 必须 是前导键列。

现在应该避免死锁,因为它们将锁定索引中的不同键,而且查询将更快地完成并且不太可能发生死锁。

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