DB,Audit和AuditField中存在两个表,以下是Create表代码:
-- 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
当此事务并发运行时,发生死锁,一个进程失败,另一个进程出错:
消息547,级别16,状态0,行40 INSERT语句冲突 使用FOREIGN KEY约束“FK_AuditFiled_Audit”。冲突 发生在数据库“MyDB”,表“dbo.Audit”,列'ID'。
Audit和AuditField表上没有触发器。 很抱歉代码的格式,我真的需要一个答案,为什么会发生这种死锁,谢谢。
有一件事应该是清楚的,AuditField表的数据来自@auditField,As @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
答案 0 :(得分:1)
这是和读 - 写死锁:
如您所见,每个事务都已成功获取[e] X [clusive]锁,并且它会请求S [hared]锁。问题是为什么事务尝试读取由另一个事务锁定的行X.
答案如下: 1)以下一段源代码
declare @lastIdentity int = @@identity
declare @offSet int = @lastIdentity - @rowCount
假设每个
生成IDENTITY
个值
INSERT INTO dbo.Audit
SELECT TypeName
FROM ...
声明仍在继续。这完全错了,如下图所示:
这意味着在某个时间点,事务可以成功地在插入的行上获得X锁定然后 1)因为插入审计的行不是连续的 2)因为
declare @lastIdentity int = @@identity
declare @offSet int = @lastIdentity - @rowCount
INSERT INTO dbo.AuditField
SELECT AuditID+@offSet AS AuditID, Field1 ...
这最后INSERT
尝试插入属于另一个事务的dbo.AuditField
,AuditID
值,这需要FK
验证,并且还意味着SQL Server需要从dbo.Audit
读取行。对于这个S [hared]需要锁。
要明确:此死锁的根本原因不是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
答案 1 :(得分:0)
您正在尝试将无效的外键值插入dbo.AuditField:
SELECT AuditID+@offSet AS AuditID, Field1
为何选择@offset?您不一定在dbo.Audit表中使用具有该值的AuditId。