SQL Service Broker内部激活问题

问题描述 投票:0回答:1

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

问题:

  1. 是否可以对两个存储过程使用一个目标队列,以防止出现死锁的可能性?]] >>

  2. 我有什么办法使它更可靠?像一个执行错误不应停止传入的请求到队列?

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

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

  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
    

    谢谢,

库兹

我为两个存储过程设置了内部激活。一个,插入一个或多个记录,另一个,更新同一表中的一个或多个记录。因此,我有两个启动器,两个目标队列。它...

是否有一种方法可以对两个存储过程使用一个目标队列,以防止出现死锁的可能性?

您可以而且应该。没有理由拥有两个目标服务/队列/过程。将两个不同的消息类型

发送到同一服务,以执行所需的两个操作。然后,激活的过程应根据消息类型执行“添加”逻辑或“更新”逻辑。

我有什么办法使它更可靠?像一个执行错误不应该停止传入请求到队列?

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

提高可伸缩性的提示(每秒高执行量)?

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

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

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

MAX_QUEUE_READERS = 1,-或10?

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

BTW,您还必须在启动程序服务队列上具有一个已激活的过程。参见How to prevent conversation endpoint leaks

sql sql-server internal service-broker activation
1个回答
3
投票

是否有一种方法可以对两个存储过程使用一个目标队列,以防止出现死锁的可能性?

© www.soinside.com 2019 - 2024. All rights reserved.