使用 SQL 服务器插入/更新死锁
Insert / update Deadlock with SQL Server
我有一个 table A
(id int, domain nvarchar, status nvarchar
) 和一个触发器 A_trigger
after insert on table A.触发器调用一个存储过程和根据过程的结果,更新新插入行的状态。
当我在两个会话中 运行 时,我遇到了死锁问题:
隔离级别:读提交
INSERT
语句:
INSERT INTO dbo.TEST_TRIGGER (DOMAIN)
VALUES ('toto')
触发器:
CREATE TRIGGER dbo.dim_trigger
ON db.dbo.TEST_TRIGGER
AFTER INSERT
AS
DECLARE @status nvarchar(200),
@domain nvarchar(200),
@trackingId int
BEGIN
SET NOCOUNT ON;
-- This code assumes we insert one and only one row at a time.
SELECT @trackingId = id, @domain = domain FROM INSERTED;
DECLARE @toCallProcName NVARCHAR(200);
SET @toCallProcName = 'db.dbo.'+@domain+'_proc';
EXEC @toCallProcName @status out;
UPDATE db.dbo.TEST_TRIGGER
SET status = @status
WHERE id = @trackingId;
END
我试过:
- 使用
WITH (UPDLOCK)
发出更新语句,但这不起作用
- 在 (ID) 上创建索引可行,但我担心这个解决方案!!
EDIT1:
Table 架构:
CREATE TABLE [dbo].[TEST_TRIGGER]
(
[DOMAIN] [NVARCHAR](200) NOT NULL,
[ID] [BIGINT] IDENTITY(1,1) NOT NULL,
[STATUS] [NVARCHAR](100) NULL
)
存储过程:
CREATE PROCEDURE [dbo].[toto_proc]
@res NVARCHAR(200) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
WAITFOR DELAY '00:00:5'
PRINT 'This is me: '+CONVERT(VARCHAR(8), GETDATE(), 108) ;
SET @res = 'OK'
END
有什么帮助吗?
谢谢
您需要做的是在 table
上创建一个索引
示例:
CREATE INDEX IX_Test_Trigger ON dbo.TEST_TRIGGER(Id)
阅读此内容以了解有关索引及其对锁定机制的影响的更多信息:
https://www.mssqltips.com/sqlservertip/2517/using-a-clustered-index-to-solve-a-sql-server-deadlock-issue/
您的问题的原因是,触发器一直保持对基座的锁定 table。由于多个会话和相同的资源,这必然会发生。在 table 上没有索引将导致每次对每个不同的会话进行 table 扫描并导致死锁。应用索引是正确的做法,因为这样你的更新语句就会达到粒度级别。所以这应该有效。我建议在您的更新语句中添加额外的 RowLock 提示。
重要提示:我假设,每个插入将只插入一个值。否则这个触发器有问题。 (如marc_s所述)
我有一个 table A
(id int, domain nvarchar, status nvarchar
) 和一个触发器 A_trigger
after insert on table A.触发器调用一个存储过程和根据过程的结果,更新新插入行的状态。
当我在两个会话中 运行 时,我遇到了死锁问题:
隔离级别:读提交
INSERT
语句:
INSERT INTO dbo.TEST_TRIGGER (DOMAIN)
VALUES ('toto')
触发器:
CREATE TRIGGER dbo.dim_trigger
ON db.dbo.TEST_TRIGGER
AFTER INSERT
AS
DECLARE @status nvarchar(200),
@domain nvarchar(200),
@trackingId int
BEGIN
SET NOCOUNT ON;
-- This code assumes we insert one and only one row at a time.
SELECT @trackingId = id, @domain = domain FROM INSERTED;
DECLARE @toCallProcName NVARCHAR(200);
SET @toCallProcName = 'db.dbo.'+@domain+'_proc';
EXEC @toCallProcName @status out;
UPDATE db.dbo.TEST_TRIGGER
SET status = @status
WHERE id = @trackingId;
END
我试过:
- 使用
WITH (UPDLOCK)
发出更新语句,但这不起作用 - 在 (ID) 上创建索引可行,但我担心这个解决方案!!
EDIT1:
Table 架构:
CREATE TABLE [dbo].[TEST_TRIGGER]
(
[DOMAIN] [NVARCHAR](200) NOT NULL,
[ID] [BIGINT] IDENTITY(1,1) NOT NULL,
[STATUS] [NVARCHAR](100) NULL
)
存储过程:
CREATE PROCEDURE [dbo].[toto_proc]
@res NVARCHAR(200) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
WAITFOR DELAY '00:00:5'
PRINT 'This is me: '+CONVERT(VARCHAR(8), GETDATE(), 108) ;
SET @res = 'OK'
END
有什么帮助吗?
谢谢
您需要做的是在 table
上创建一个索引示例:
CREATE INDEX IX_Test_Trigger ON dbo.TEST_TRIGGER(Id)
阅读此内容以了解有关索引及其对锁定机制的影响的更多信息: https://www.mssqltips.com/sqlservertip/2517/using-a-clustered-index-to-solve-a-sql-server-deadlock-issue/
您的问题的原因是,触发器一直保持对基座的锁定 table。由于多个会话和相同的资源,这必然会发生。在 table 上没有索引将导致每次对每个不同的会话进行 table 扫描并导致死锁。应用索引是正确的做法,因为这样你的更新语句就会达到粒度级别。所以这应该有效。我建议在您的更新语句中添加额外的 RowLock 提示。