Zap*_*002 4 sql-server deadlock insert update sql-server-2014
我有一个表 (Database2.dbo.OrganizerDataDependencyChange),其中包含有关某些其他表中的行上次更改时间的信息。在这些表中的每一个上,我都有一个触发器,它调用一个存储过程 (Database2.dbo.SaveOrganizerDataDependencyChange),该过程简单地用触发器触发的时间更新 Database2.dbo.OrganizerDataDependencyChange。这些触发器是从 Database1 或 Database2 触发的,因此它们通常是跨数据库调用。
当更新表上的两个不同行时,我在 Database1.dbo.OrganizerDataDependencyChange 表上遇到读写死锁,我不明白为什么。表上只有一个索引,而且它是完全覆盖查询的聚集索引,因此不可能出现查找死锁,尽管 proc 中有两条语句,但我特地将其重写为我所理解的最佳方法避免并发问题,使用 WHERE NOT EXISTS 而不是使用 IF/ELSE 逻辑,所以它不应该从不同的角度出现在这个索引上,对吗?由于它是在触发器和跨数据库中触发的,因此我在重现该问题时遇到了一些困难,我当然可以重现阻塞,但这应该没问题,这正是我所期望的。
有人可以帮助我了解这里发生了什么吗?我可能可以使用 NOLOCK 提示或 applock 修复它,但我仍然不明白为什么会发生这种情况。
这是表:
CREATE TABLE [dbo].[OrganizerDataDependencyChange](
[TableID] [INT] NOT NULL,
[Database] [VARCHAR](150) NOT NULL,
[Updated] [DATETIME] NULL
)
CREATE CLUSTERED INDEX [CX_OrganizerDataDependencyChange_TableID_DB] ON [dbo].[OrganizerDataDependencyChange]
(
[TableID] ASC,
[Database] ASC
)
Run Code Online (Sandbox Code Playgroud)
这是存储过程:
CREATE PROCEDURE [dbo].[SaveOrganizerDataDependencyChange]
(
@TableID int,
@Database varchar(150)
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @rowcount INT;
INSERT INTO dbo.OrganizerDataDependencyChange
( TableID, [Database], Updated )
SELECT TOP 1
TableID = @TableID,
[Database] = ISNULL(@Database, ''),
Updated = GETDATE()
FROM dbo.OrganizerDataDependencyChange
WHERE NOT EXISTS
(
SELECT
1
FROM dbo.OrganizerDataDependencyChange
WHERE TableID = @TableID
AND [Database] = @Database
)
SET @rowcount = @@ROWCOUNT --How many rows were inserted?
UPDATE dbo.OrganizerDataDependencyChange
SET Updated = GETDATE()
WHERE @rowcount = 0 --Only run this if no rows were inserted
AND TableID = @TableID
AND [Database] = ISNULL(@Database, '')
END
Run Code Online (Sandbox Code Playgroud)
最后,我从 XE 中得到的几个死锁图显示了荒谬的嵌套触发器:
MSSQL2014 标准,如果这有区别的话。隔离级别是默认的 Read Committed。如果还有什么我可以分享以阐明更多信息,请告诉我!
我想你会更好地发现这种模式:
BEGIN TRANSACTION;
UPDATE dbo.OrganizerDataDependencyChange WITH (HOLDLOCK)
SET Updated = GETDATE()
WHERE TableID = @TableID
AND [Database] = ISNULL(@Database, '');
IF @@ROWCOUNT = 0
BEGIN
INSERT INTO dbo.OrganizerDataDependencyChange
( TableID, [Database], Updated )
VALUES(@TableID, ISNULL(@Database, ''), GETDATE());
END
COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)
不要再考虑必须“检查”是否有要更新的行,然后再更新它,这可能会导致两次完整扫描。只是尝试更新它。如果没有行被更新,没有伤害,没有犯规 - 您可以简单地执行插入(我在这里稍微讨论一下)。
归档时间: |
|
查看次数: |
4307 次 |
最近记录: |