发生SQL死锁,使用三个触发器,跨服务器,没有找到原因

时间:2018-01-18 13:21:32

标签: sql sql-server database

  
      
  1. 我在Server-A上的Database-A的Table-A上使用了Insert-trigger。此触发器将Inserted-data放入表的-B   Server-B上的Database-B。
  2.   
  3. 我在Server-B上的Database-B的Table-B上使用了Insert-trigger。此触发器使用Inserted-data进行计算,然后使用   将结果插入Server-B上Database-B的Table-C。
  4.   
  5. 我在Server-B上的Database-B的Table-C上使用了Update-trigger。此触发器使用Inserted-data进行计算,然后使用   自动发送邮件。
  6.   

SQL Server现在看起来好像经常死锁。有什么好的解决方案吗?

说明:第一个触发器用于获取数据,第二个触发器用于实际逻辑,第三个触发器根据第二个触发器的结果发送邮件。

在Server-A上的数据库A的表A上触发如下:

RELEASE

在Server-B上触发数据库B的表B,如下所示:

USE [Database-A]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TRIGGER [dbo].[TRIGGER_INSERT_A]
  ON [dbo].[Table-A]
  FOR INSERT
AS
  BEGIN
    INSERT INTO [Server-B].[Database-B].[dbo].[Table-B] (
        [Server-B].[dbo].[Table-B].[field1],
        [Server-B].[dbo].[Table-B].[field2],
        [Server-B].[dbo].[Table-B].[field3],
        [Server-B].[dbo].[Table-B].[field4],
        [Server-B].[dbo].[Table-B].[field5],
        [Server-B].[dbo].[Table-B].[field6],
        [Server-B].[dbo].[Table-B].[field7]
    ) SELECT
        [field1],
        [field2],
        [field3],
        [field4],
        [field5],
        [field6],
        [field7]
        FROM inserted
  END
GO

在Server-B上触发Database-B的Table-C,如下所示:

USE [Database-B]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TRIGGER [dbo].[TRIGGER_INSERT_B]
ON [dbo].[Table-B]
AFTER INSERT
AS
BEGIN
  SET NOCOUNT ON;

  -- Logic portion is omitted
  -- UPDATE [dbo].[Table-B] OR INSERT [dbo].[Table-B]
END

GO

2 个答案:

答案 0 :(得分:1)

在链接服务器上使用触发器是个糟糕的主意。除了死锁之外,还有很多事情可能会出错。

最有可能的是,插入到表A中的事务由于插入ServerB.TableB的延迟而保持更长时间。如果链接服务器连接因任何原因而变慢,则会遇到问题。

如果TableA和TableB需要如此紧密地链接,它们应该在同一台服务器上,如果它们在同一个数据库中则更好。跨越物理服务器边界将为极其快速的过程增加显着的开销,即使在快速网络中,也可以轻松地从几微秒变为5毫秒或更长。这可能看起来不多,但持续时间增加1000%会产生影响。此外,即使是最好的网络也可能会暂时降低性能。突然延迟增加到100毫秒会使您的性能突然停止,因为您可能会遇到事务超时。

是否有任何理由将表B中的插入作为插入表A的同一事务的一部分?我认为在服务器之间移动数据最好由一个独立的进程处理,这意味着不是表A的事务的一部分。我建议一个频繁运行的ETL进程,可能每分钟运行一次。如果这太慢,你真的应该把它们整合到同一个基础设施上。

根据您发布的代码,我不知道TableC与死锁的关系。 TableA和TableB的触发器似乎无法与之交互。

答案 1 :(得分:0)

我完全同意Wes H的意见,但我不喜欢拥有高频ETL的想法。如果插入每天发生一次,则会浪费大量资源。

根据您的脚本,您正在复制另一台服务器上的整个表,那么为什么不实际使用复制呢?

否则,这似乎是SERVER BROKER的一个场景。 https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-service-broker 您可以直接在触发器内创建消息,并将其与其他服务器异步使用。