gpt4 book ai didi

sql-server - 表存在外键时发生死锁

转载 作者:搜寻专家 更新时间:2023-10-30 20:40:00 24 4
gpt4 key购买 nike

DB中有两个表,Audit和AuditField,下面是建表代码:

-- Primary key: ID
CREATE TABLE [dbo].[Audit](
[ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[TypeName] [varchar](50) NOT NULL
)
GO

-- Primary key: ID
CREATE TABLE [dbo].[AuditField](
[ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[AuditID] [int] NOT NULL,
[Field1] [varchar](50) NOT NULL
)

GO
-- Set foreign key on AuditField table
ALTER TABLE [dbo].[AuditField]
ADD CONSTRAINT [FK_AuditFiled_Audit] FOREIGN KEY([AuditID])
REFERENCES [dbo].[Audit] ([ID])
GO

然后我准备了一些测试数据:

DECLARE @audit TABLE
(
ID int not null,
TypeName varchar(50)
)

DECLARE @auditField TABLE
(
AuditID int not null,
Field1 varchar(50)
)

-- ADD TEST DATA
DECLARE @i int = 1
DECLARE @rowCount int = 500
WHILE @i<=@rowCount
BEGIN
INSERT INTO @audit
VALUES(@i, 'SomeTypeName')

INSERT INTO @auditField
(AuditID,Field1)
VALUES(@i,'SomeThing')

SET @i += 1
END

最后,我运行以下事务以将测试数据插入到这两个表中:

begin transaction
INSERT INTO dbo.Audit
SELECT TypeName
FROM @audit
ORDER BY ID

declare @lastIdentity int = @@identity
declare @offSet int = @lastIdentity - @rowCount

INSERT INTO dbo.AuditField
SELECT AuditID+@offSet AS AuditID, Field1
FROM @auditField
ORDER BY AuditID
commit transaction

当这个事务并发运行时,出现死锁,一个进程失败,另一个进程出错:

Msg 547, Level 16, State 0, Line 40 The INSERT statement conflicted with the FOREIGN KEY constraint "FK_AuditFiled_Audit". The conflict occurred in database "MyDB", table "dbo.Audit", column 'ID'.

Audit 和 AuditField 表上没有触发器。对不起代码的格式,我真的需要一个答案为什么会发生这个死锁,谢谢。

有一点要明确,AuditField表的数据来自于@auditField,@Bogdan的回答我是这样重写的:

begin transaction
INSERT INTO dbo.Audit
OUTPUT inserted.ID INTO @temp
SELECT TypeName
FROM @audit

INSERT INTO @idMapping
SELECT ROW_NUMBER() OVER(ORDER BY ID) AS RowNumber, ID
FROM @temp

INSERT INTO dbo.AuditField
SELECT m.ID AS AuditID, Field1
FROM @auditField af
INNER JOIN @idMapping m ON af.AuditID = m.RowNumber

commit transaction

最佳答案

这是和读-写死锁: enter image description here

如你所见,每个事务都成功获取了一个[e]X[clusive]锁,并请求了一个S[hared]锁。问题是为什么一个事务试图读取被另一个事务锁定的行 X。

答案如下:1) 以下一段源码

declare @lastIdentity int = @@identity
declare @offSet int = @lastIdentity - @rowCount

假设 IDENTITY 值由每个生成

INSERT INTO dbo.Audit
SELECT TypeName
FROM ...

声明还在继续。这是完全错误的,如下图所示:

enter image description here

这意味着在某个时间点,事务可以成功地在插入的行上获得 X 锁,然后1)因为插入到 Audit 中的行不是连续的并且2)因为

declare @lastIdentity int = @@identity
declare @offSet int = @lastIdentity - @rowCount

INSERT INTO dbo.AuditField
SELECT AuditID+@offSet AS AuditID, Field1 ...

这最后一个 INSERT 尝试将属于另一个事务的 AuditID 值插入到 dbo.AuditField 中,这需要 FK 验证,也意味着 SQL Server 需要从 dbo.Audit 读取行。为此,需要 S[共享] 锁。

需要明确的是:这个死锁的根本原因不是 FK 约束。真正的问题是源代码。

解决方案:我会这样重写:

begin transaction
INSERT INTO dbo.Audit
OUTPUT inserted.ID, inserted.TypeName INTO @audit (ID, TypeName)
SELECT TypeName
FROM @audit
-- ORDER BY ID -- Isn't necessary

... do something (ex. DELETE) with rows from @audit

INSERT INTO dbo.AuditField (AuditID, ...)
SELECT x.ID, ...
FROM @audit x
-- ORDER BY AuditID

/* or
INSERT INTO dbo.AuditField (AuditID, Field1, ....)
SELECT y.ID, y.ColumnName, ...
FROM (
SELECT x.ID, ...
FROM @audit x
UNPIVOT( ColumnValue FOR ColumnName IN ([TypeName], ...) )
) y
WHERE y.....
*/
commit transaction -- Isn't necessary

关于sql-server - 表存在外键时发生死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24535822/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com