【问题标题】:Insert / update Deadlock with SQL Server使用 SQL Server 插入/更新死锁
【发布时间】:2019-01-14 07:41:58
【问题描述】:

在表 A 上插入后,我有一个表 A (id 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

有什么帮助吗?

谢谢

【问题讨论】:

  • 每行创建一个索引?我认为您不了解索引是什么。
  • 请启发我 :) 我不是专业人士
  • 向我们展示你实际做了什么 - 每行创建索引,因为我不知道你想做什么。
  • CREATE INDEX i1_TEST_TRIGGER ON dbo.TEST_TRIGGER (id) 也许我应该说“索引的大小会增长得非常快”而不是“..需要为每行创建一个索引”。可能我也很不明白
  • 您的触发器有 MAJOR 缺陷,您似乎认为它会被 每行调用一次 - 事实并非如此。触发器将每条语句触发一次,因此如果您的INSERT 导致此触发器触发插入 25 行,您将触发触发器一次和@987654331 @ 伪表将包含 25 行。您的代码将从Inserted 中选择这 25 行中的哪一行?它是不确定的,您将获得任意行,而您将忽略所有其他行。您需要重写触发器以考虑到这一点!

标签: sql sql-server database-deadlocks


【解决方案1】:

你需要做的是在你的表上创建一个索引

示例:

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/

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 2019-07-11
      • 1970-01-01
      • 2015-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多