使用SQL Server插入/更新死锁

时间:2018-08-07 09:10:16

标签: sql sql-server database-deadlocks

在表A上插入后,我有一个表Aid int, domain nvarchar, status nvarchar)和触发器A_trigger。触发器调用存储过程,并根据过程的结果更新新插入的行上的状态。

当我在两个会话中运行它时,最终会遇到死锁问题:

隔离级别:读取已提交

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

表架构:

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

有帮助吗?

谢谢

2 个答案:

答案 0 :(得分:1)

您需要做的是在表上创建索引

示例:

CREATE INDEX IX_Test_Trigger ON dbo.TEST_TRIGGER(Id)

请阅读以下内容以获取有关Index及其对锁定机制的影响的更多信息: https://www.mssqltips.com/sqlservertip/2517/using-a-clustered-index-to-solve-a-sql-server-deadlock-issue/

答案 1 :(得分:1)

您遇到的问题的原因是,触发器将锁保持在基表上。由于多个会话和相同的资源,这势必会发生。在表上没有索引将导致每次针对每个不同的会话进行表扫描并导致死锁。应用索引是正确的举动,因为这样您的update语句将达到粒度级别。所以这应该工作。我建议在更新语句中额外提示RowLock。

重要:我假设,每个插入将只有一个值要插入。否则,此触发器存在问题。 (如marc_s所述)