Yis*_*hai 4 sql-server-2005 foreign-key deadlock
所以我有两个表会造成死锁。应用程序实际上什么都不做,只是在两个不同的事务中更新两个不同的表。这两个表之间存在外键约束(不是级联,只是为了强制完整性),否则就每个事务的发生方式而言,两者之间没有关系。这可能是僵局的全部根源吗?如果是这样,你如何设计外键约束来避免这个问题?
更新:我应该指出(对于那些遇到这个问题的人)根本问题最终与外键约束无关,但是根据这一点,如果另一个外键约束可能导致死锁side 只有主键的聚集索引。
我还了解到死锁 XML 报告没有捕获到那时的整个事务(我错误的假设它确实导致了不正确的问题),这很烦人。
编辑:这两个表是一个名为 KID 的表和一个名为 Image 的表(还有 ZAT_KID 和 ZAT_Image 由触发器填充作为审计跟踪,它们不引用其他任何内容)。Image 的 KIDID 字段是 KIDID 的 KID 主键(聚集索引)的外键。我还应该补充一点,每个堆栈开头的初始选择 1 是数据库池,确保连接在执行其他任何操作之前仍然有效。
这是死锁 XML:
<deadlock-list>
<deadlock victim="processedb6d8">
<process-list>
<process id="processec49b8" taskpriority="0" logused="2672" waitresource="PAGE: 7:1:295182" waittime="2609" ownerId="2771483341" transactionname="implicit_transaction" lasttranstarted="2013-02-05T15:32:48.150" XDES="0x804da410" lockMode="X" schedulerid="2" kpid="10180" status="suspended" spid="93" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2013-02-05T15:32:56.980" lastbatchcompleted="2013-02-05T15:32:56.963" clientapp="Microsoft JDBC Driver for SQL Server" hostname="newappserver" hostpid="0" loginname="DatabaseUser" isolationlevel="read committed (2)" xactid="2771483341" currentdb="7" lockTimeout="4294967295" clientoption1="539099168" clientoption2="128058">
<executionStack>
<frame procname="Database.dbo.T_Audit_KID" line="67" stmtstart="3936" stmtend="5062" sqlhandle="0x030007005776f42cc93b87015aa100000000000000000000">
INSERT INTO ZAT_KID with (PAGLOCK) (KIDID, KID, KosherStatusID, restrictionText,expirationDate,issuedDate,name,brand,void,rabbiSigner,agencyID,vendorID, ActivityDate, ActivityByID,Mode ,Extended,TempOwner,SecLevel,Source,Dataless,Overridden,UKDAgencyCode,UKDAgencyUniqueID)
VALUES ( @KIDID, @KID, @KosherStatusID, @restrictionText,@expirationDate,@issuedDate,@name,@brand,@void,@rabbiSigner,@agencyID,@vendorID, @ActivityDate, @ActivityByID,@Mode, @Extended,@TempOwner, @SecLevel,@Source,@Dataless,@Overridden,@UKDAgencyCode,@UKDAgencyUniqueID) </frame>
<frame procname="adhoc" line="1" stmtstart="60" sqlhandle="0x02000000285d0519fdc120b4e4bbeca4c48d20fa089a34b5">
UPDATE kid SET activityByID=@P0, activityDate=@P1 WHERE kidID=@P2 </frame>
<frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
Select 1 </inputbuf>
</process>
<process id="processedb6d8" taskpriority="0" logused="1728" waitresource="PAGE: 7:1:295211" waittime="3781" ownerId="2771489653" transactionname="implicit_transaction" lasttranstarted="2013-02-05T15:32:55.683" XDES="0x2ce89f710" lockMode="X" schedulerid="3" kpid="9452" status="suspended" spid="64" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2013-02-05T15:32:55.807" lastbatchcompleted="2013-02-05T15:32:55.713" clientapp="Microsoft JDBC Driver for SQL Server" hostname="newappserver" hostpid="0" loginname="DatabaseUser" isolationlevel="read committed (2)" xactid="2771489653" currentdb="7" lockTimeout="4294967295" clientoption1="539099168" clientoption2="128058">
<executionStack>
<frame procname="Database.dbo.T_Audit_Image" line="49" stmtstart="2178" stmtend="2612" sqlhandle="0x030007003c4d5a39f33b87015aa100000000000000000000">
INSERT INTO ZAT_Image with (PAGLOCK) (ImageID, KIDID, ExpirationDate, Exclude, ActivityByID, ActivityDate,Mode)
VALUES ( @ImageID, @KIDID,@ExpirationDate, @Exclude, @ActivityByID, @ActivityDate,@Mode) </frame>
<frame procname="adhoc" line="1" stmtstart="174" sqlhandle="0x020000006641f2279ebb048f51cb23b67af52ff7c3599d05">
insert into Image (ImageStoreKey, ActivityByID, ActivityDate, exclude, ExpirationDate, KIDID, ImageID) values (@P0, @P1, @P2, @P3, @P4, @P5, @P6) </frame>
<frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
Select 1 </inputbuf>
</process>
</process-list>
<resource-list>
<pagelock fileid="1" pageid="295182" dbid="7" objectname="Database.dbo.ZAT_KID" id="lock16ec04700" mode="X" associatedObjectId="72057594481606656">
<owner-list>
<owner id="processedb6d8" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="processec49b8" mode="X" requestType="wait"/>
</waiter-list>
</pagelock>
<pagelock fileid="1" pageid="295211" dbid="7" objectname="Database.dbo.ZAT_Image" id="lock6e16cea00" mode="X" associatedObjectId="72057594441498624">
<owner-list>
<owner id="processec49b8" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="processedb6d8" mode="X" requestType="wait"/>
</waiter-list>
</pagelock>
</resource-list>
</deadlock>
</deadlock-list>
Run Code Online (Sandbox Code Playgroud)
进一步编辑:以下是两个表架构:
CREATE TABLE [dbo].[Image](
[ImageID] [int] NOT NULL,
[KIDID] [int] NULL,
[ExpirationDate] [datetime] NULL,
[Exclude] [nchar](1) NULL,
[ActivityDate] [datetime] NULL,
[ActivityByID] [int] NULL,
[ImageStoreKey] [nvarchar](255) NULL,
CONSTRAINT [PK_Image2] PRIMARY KEY CLUSTERED
(
[ImageID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[Image] WITH NOCHECK ADD CONSTRAINT [FK_Image_KID] FOREIGN KEY([KIDID])
REFERENCES [dbo].[KID] ([KIDID])
GO
ALTER TABLE [dbo].[Image] CHECK CONSTRAINT [FK_Image_KID]
GO
Run Code Online (Sandbox Code Playgroud)
和 KID 表:
CREATE TABLE [dbo].[KID](
[KIDID] [int] NOT NULL,
[KID] [nchar](7) NULL,
[KosherStatusID] [int] NULL,
[RestrictionText] [nvarchar](255) NULL,
[ExpirationDate] [datetime] NULL,
[IssuedDate] [datetime] NULL,
[Name] [nvarchar](255) NULL,
[Brand] [nvarchar](255) NULL,
[Void] [char](1) NULL,
[RabbiSigner] [nvarchar](255) NULL,
[Extended] [nchar](1) NULL,
[AgencyID] [int] NULL,
[VendorID] [int] NULL,
[PublicVisible] [nchar](1) NULL,
[TempOwner] [nvarchar](20) NULL,
[Source] [nvarchar](20) NULL,
[Dataless] [nvarchar](1) NULL,
[Overridden] [nchar](1) NULL,
[ActivityDate] [datetime] NULL,
[ActivityByID] [int] NULL,
[UKDAgencyCode] [nchar](12) NULL,
[UKDAgencyUniqueID] [nvarchar](50) NULL,
[SecLevel] [int] NOT NULL,
CONSTRAINT [PK_KID] PRIMARY KEY CLUSTERED
(
[KIDID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
ZAT 表:
CREATE TABLE [dbo].[ZAT_KID](
[ZAT_KID_ID] [int] IDENTITY(1,1) NOT NULL,
[KIDID] [int] NOT NULL,
[KID] [nchar](7) NULL,
[KosherStatusID] [int] NULL,
[RestrictionText] [nvarchar](255) NULL,
[ExpirationDate] [datetime] NULL,
[IssuedDate] [datetime] NULL,
[Name] [nvarchar](255) NULL,
[Brand] [nvarchar](255) NULL,
[Void] [char](1) NULL,
[RabbiSigner] [nvarchar](255) NULL,
[Extended] [nchar](1) NULL,
[AgencyID] [int] NULL,
[VendorID] [int] NULL,
[PublicVisible] [nchar](1) NULL,
[TempOwner] [nvarchar](20) NULL,
[Source] [nvarchar](20) NULL,
[Dataless] [nvarchar](1) NULL,
[Overridden] [nchar](1) NULL,
[ActivityDate] [datetime] NULL,
[ActivityByID] [int] NULL,
[Mode] [nvarchar](20) NULL,
[UKDAgencyCode] [nchar](12) NULL,
[UKDAgencyUniqueID] [nvarchar](50) NULL,
[SecLevel] [int] NULL,
CONSTRAINT [PK_ZAT_KID] PRIMARY KEY CLUSTERED
(
[ZAT_KID_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[ZAT_Image](
[ZAT_Image_ID] [int] IDENTITY(1,1) NOT NULL,
[ImageID] [int] NOT NULL,
[KIDID] [int] NULL,
[ActivityDate] [datetime] NULL,
[ActivityByID] [int] NULL,
[Mode] [nvarchar](20) NULL,
[ExpirationDate] [datetime] NULL,
[Exclude] [nchar](1) NULL,
CONSTRAINT [PK_ZAT_ImageID] PRIMARY KEY CLUSTERED
(
[ZAT_Image_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
UPDATE kid SET activityByID=@P0, activityDate=@P1 WHERE kidID=@P2
触发器INSERT INTO ZAT_KID with (PAGLOCK)
insert into Image ...
触发器INSERT INTO ZAT_Image with (PAGLOCK)
T1 想要 X 页 295182 (ZAT_KID) 并且有 X 页 295211 (ZAT_Image)。T2 需要 X 页 295211 (ZAT_Image) 并且在 295182 (ZAT_KID) 上有 X。这表明所涉及的两个 spid 的先前活动在这里起作用:T1 先前已锁定 ZAT_Image 上的一个页面,但当前堆栈与该表无关。类似地,T2 之前在 ZAT_Image 上锁定了一个页面,但当前堆栈与它无关。
场景非常清楚:虚假使用理解不足的提示。T1锁定了ZAT_Image的插入点,而T2锁定了ZAT_KID的插入点。僵局是不可避免的。INSERT WITH (PAGLOCK)
是罪魁祸首。你在玩火,你被烧伤了。停止使用您不理解的提示。去掉 PAGLOCK 提示,让引擎决定合适的策略。保留审计表中的插入点空闲以供进一步插入。用棍子击败将 PAGLOCK 添加到 INSERT 的人。