【问题标题】:SQL Service Broker Internal Activation QuestionsSQL Service Broker 内部激活问题
【发布时间】:2014-05-15 07:19:57
【问题描述】:

我为两个存储过程设置了内部激活。一是插入一条或多条记录,二是更新同一张表中的一条或多条记录。所以,我有两个发起者,两个目标队列。 到目前为止它在开发中运行良好,但我想知道当我们将它移到经常调用这两个存储过程的 prod 时我可能会遇到什么类型的问题。我们已经遇到了由这两个存储过程引起的死锁问题。异步执行是我这个实现的主要目标。

问题:

  1. 有没有办法为两个存储过程使用一个目标队列来防止出现死锁?

  2. 我可以做些什么来使它更可靠?像一个执行错误不应该停止传入的请求 去排队?

  3. 提高可扩展性(每秒执行次数较多)的技巧?

  4. 如果出现死锁,我可以设置 RETRY 吗?

这里是插入存储过程的部分代码;

CREATE QUEUE   [RecordAddUsersQueue];
CREATE SERVICE [RecordAddUsersService] ON QUEUE [RecordAddUsersQueue];

ALTER QUEUE [AddUsersQueue] WITH ACTIVATION 
(     STATUS            = ON,
      MAX_QUEUE_READERS = 1, --or 10?
      PROCEDURE_NAME    = usp_AddInstanceUsers,
      EXECUTE AS OWNER);

CREATE PROCEDURE [dbo].[usp_AddInstanceUsers] @UsersXml xml
AS
BEGIN
  DECLARE @Handle uniqueidentifier;

  BEGIN DIALOG CONVERSATION @Handle
  FROM SERVICE [RecordAddUsersService]
  TO   SERVICE 'AddUsersService'
  ON  CONTRACT [AddUsersContract]
  WITH ENCRYPTION = OFF;

  SEND ON CONVERSATION @Handle
  MESSAGE TYPE [AddUsersXML] (@UsersXml);
END
GO

CREATE PROCEDURE [dbo].[usp_SB_AddInstanceUsers]
AS
BEGIN
  DECLARE @Handle uniqueidentifier;
  DECLARE @MessageType sysname;
  DECLARE @UsersXML xml;

  WHILE (1 = 1)
  BEGIN
    BEGIN TRANSACTION;
      WAITFOR
      (RECEIVE TOP (1)
      @Handle      = conversation_handle,
      @MessageType = message_type_name,
      @UsersXML    = message_body
      FROM [AddUsersQueue]), TIMEOUT 5000;
      IF (@@ROWCOUNT = 0)
      BEGIN
        ROLLBACK TRANSACTION;
        BREAK;
      END

      IF (@MessageType = 'ReqAddUsersXML')
      BEGIN
        --<INSERT>....
        DECLARE @ReplyMsg nvarchar(100);
        SELECT
          @ReplyMsg = N'<ReplyMsg>Message for AddUsers Initiator service.</ReplyMsg>';
        SEND ON CONVERSATION @Handle
        MESSAGE TYPE [RepAddUsersXML] (@ReplyMsg);
      END

      ELSE
      IF @MessageType = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
      BEGIN
        END CONVERSATION @Handle;
      END
      ELSE
      IF @MessageType = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error'
      BEGIN
        END CONVERSATION @Handle;
      END
    COMMIT TRANSACTION;
  END
END
GO

谢谢,

库兹

【问题讨论】:

    标签: sql sql-server internal service-broker activation


    【解决方案1】:

    有没有办法为两个存储过程使用一个目标队列来防止出现死锁?

    你可以而且你应该。没有理由拥有两个目标服务/队列/过程。将两种不同的消息类型发送到同一个服务,用于您想要的两种操作。然后,激活的过程应执行 Add 逻辑或 Update 逻辑,具体取决于消息类型。

    我能做些什么让它更可靠吗?比如一个执行错误不应该停止传入队列的请求?

    SSB 激活将非常可靠,这不会成为问题。只要您严格遵守事务边界(在处理完成之前不要提交出队操作),您就永远不会丢失消息/更新。

    提高可扩展性的技巧(每秒执行的高次数)?

    阅读Writing Service Broker ProceduresReusing Conversations。要实现高吞吐量处理,您将必须分批 (TOP(1000)) 出列并处理到 @table 变量中。请参阅Exception handling and nested transactions 了解可用于处理一批消息的模式。您需要阅读并理解Conversation Group Locks

    如果出现死锁,我可以设置 RETRY 吗?

    不需要,SSB 激活会为您重试。当您回滚时,出队 (RECEIVE) 将回滚,从而使消息再次可用于激活,并且该过程将自动重试。注意连续5次回滚会触发poison message trap

    MAX_QUEUE_READERS = 1,--或 10?

    如果 1 无法处理负载,请添加更多。只要您了解正确的会话组锁定,并行激活的过程应该处理不相关的业务项目并且永远不会死锁。如果您在同一队列上的激活过程实例之间遇到死锁,则意味着您在会话组逻辑中存在缺陷,并且您允许 SSB 视为不相关(不同组)的消息修改相同的数据库记录(相同的业务实体)和导致死锁。

    顺便说一句,您还必须在启动器服务队列上激活一个过程。见How to prevent conversation endpoint leaks

    【讨论】:

    • “根据消息类型,激活的过程应该执行 Add 逻辑或 Update 逻辑。” 合并插入和更新存储过程将需要在应用端进行重大更改.如果应用程序仍然调用 insert 和 update sprocs,但两个 sprocs 都调用了第三个 sproc,这将是新的激活 sproc,具有不同的参数/message_types,该怎么办?
    • 我不知道你的代码,但通常应该是一个简单的过程。今天你有两个程序,每个程序都有一个块IF @messageType = 'someType' BEGIN &lt;block&gt; END。您只需合并 IF,例如 IF @messageType = 'someType' BEGIN &lt;blockFromSP1&gt; END IF @messageType = 'otherType' BEGIN &lt;blockFromSp2&gt; END
    • 制作处理句柄TOP(1000)会更复杂,但也不多。也是一种直截了当的、机械的、改造的。接收到@table,在@table 上打开游标,迭代,在游标循环内应用与以前完全相同的逻辑(IF @message Type ...)。必须小心不要过早提交,必须在提交之前处理 @table 中的所有消息,否则您可能会因错误而丢失消息。
    猜你喜欢
    • 2015-12-10
    • 1970-01-01
    • 1970-01-01
    • 2011-01-14
    • 1970-01-01
    • 1970-01-01
    • 2012-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多