SQL Service Broker内部激活问题

时间:2014-04-03 12:38:44

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

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

问题:

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

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

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

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

  5. 以下是插入存储过程的部分代码;

    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
    

    谢谢,

    Kuzey

1 个答案:

答案 0 :(得分:3)

  

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

你可以而且你应该。没有理由拥有两个目标服务/队列/程序。为所需的两个操作发送两个不同的消息类型到同一服务。然后,激活的过程应该执行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