如何在队列中使用许多作业来加速Service Broker?

时间:2014-08-01 05:09:16

标签: sql-server multithreading stored-procedures service-broker

我使用带有内部激活的SQL Service Broker将作业列表移动到内部激活的存储过程以完成,而不保持主线程/请求者等待实际的单个作业完成。本质上我正试图释放UI线程。问题是,我向服务经纪人传递了2000多个工作,并且消息在大约25分钟内到达队列并释放了用户界面,但即使一小时后,它也只完成了近600个工作岗位 我使用下面的查询来计算等待完成的数字,它看起来非常慢

SELECT COUNT(*)
FROM [HMS_Test].[dbo].[HMSTargetQueueIntAct] 
WITH(NOLOCK)

以下是我的ref的激活存储过程。有人可以看看,让我知道这有什么问题吗?如何让SB快速完成队列中的这些项目?在此先感谢:)

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_SB_HMSTargetActivProc]
AS
BEGIN
DECLARE @RecvReqDlgHandle UNIQUEIDENTIFIER;
DECLARE @RecvReqMsg NVARCHAR(1000);
DECLARE @RecvReqMsgName sysname;

DECLARE @XMLPtr int
DECLARE @ExecuteSQL nvarchar(1000)
DECLARE @CallBackSP nvarchar(100)
DECLARE @CallBackSQL nvarchar(1000)
DECLARE @SBCaller nvarchar(50)
DECLARE @LogMsg nvarchar(1000)

WHILE (1=1)
BEGIN
    BEGIN TRANSACTION;
    WAITFOR
    ( RECEIVE TOP(1)
        @RecvReqDlgHandle = conversation_handle,
        @RecvReqMsg = message_body,
        @RecvReqMsgName = message_type_name
      FROM HMSTargetQueueIntAct
    ), TIMEOUT 5000;

    IF (@@ROWCOUNT = 0)
    BEGIN
      ROLLBACK TRANSACTION;
      BREAK;
    END

    IF @RecvReqMsgName = N'//HMS/InternalAct/RequestMessage'
    BEGIN
       DECLARE @ReplyMsg NVARCHAR(100);
       SELECT @ReplyMsg = N'<ReplyMsg>ACK Message for Initiator service.</ReplyMsg>';
       SEND ON CONVERSATION @RecvReqDlgHandle
              MESSAGE TYPE 
              [//HMS/InternalAct/ReplyMessage]
              (@ReplyMsg);

        EXECUTE sp_xml_preparedocument @XMLPtr OUTPUT, @RecvReqMsg
        SELECT @ExecuteSQL = ExecuteSQL
              ,@CallBackSP = CallBackSP
              ,@SBCaller = SBCaller
        FROM OPENXML(@XMLPtr, 'RequestMsg/CommandParameters', 1)
        WITH    (ExecuteSQL nvarchar(1000) 'ExecuteSQL'
                ,CallBackSP nvarchar(1000) 'CallBackSP'
                ,SBCaller nvarchar(50) 'SBCaller'
                )
        EXEC sp_xml_removedocument @XMLPtr

        IF ((@ExecuteSQL IS NOT NULL) AND (LEN(@ExecuteSQL)>0))
        BEGIN
            SET @LogMsg='ExecuteSQL:' + @ExecuteSQL
            EXECUTE(@ExecuteSQL);
            SET @LogMsg='ExecuteSQLSuccess:' + @ExecuteSQL
            EXECute sp_LogSystemTransaction @SBCaller,@LogMsg,'SBMessage',0,''
        END
        IF ((@CallBackSP IS NOT NULL) AND (LEN(@CallBackSP)>0))
        BEGIN
            SET @CallBackSQL = @CallBackSP + ' @Sender=''sp_SB_HMSTargetActivProc'', @Res=''' + @ExecuteSQL + ''''

            SET @LogMsg='CallBackSQL:' + @CallBackSQL
            EXECute sp_LogSystemTransaction @SBCaller,@LogMsg,'SBMessage',0,''
            EXECUTE(@CallBackSQL);
        END

    END
    ELSE IF @RecvReqMsgName = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
    BEGIN
        SET @LogMsg='MessageEnd:';
        END CONVERSATION @RecvReqDlgHandle WITH CLEANUP;
    END
    ELSE IF @RecvReqMsgName = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error'
    BEGIN
        DECLARE @message_body VARBINARY(MAX);
        DECLARE @code int;
        DECLARE @description NVARCHAR(3000);
        DECLARE @xmlMessage XML;

        SET @xmlMessage = CAST(@RecvReqMsg AS XML);

        SET @code = (
              SELECT @xmlMessage.value(
                N'declare namespace
                   brokerns="http://schemas.microsoft.com/SQL/ServiceBroker/Error";
                       (/brokerns:Error/brokerns:Code)[1]', 
                'int')
                );

        SET @description = (
              SELECT @xmlMessage.value(
                'declare namespace
                   brokerns="http://schemas.microsoft.com/SQL/ServiceBroker/Error";
                   (/brokerns:Error/brokerns:Description)[1]', 
                'nvarchar(3000)')
                );


        IF (@code = -8462)
        BEGIN
            SET @LogMsg='MessageEnd:';
            --EXECute sp_LogSystemTransaction @SBCaller,@LogMsg,'SBMessage',0,'';

            END CONVERSATION @RecvReqDlgHandle WITH CLEANUP;
        END
        ELSE
        BEGIN
            SET @LogMsg='ERR:' + @description + ' ' + CAST(@code AS VARCHAR(20));
            EXECute sp_LogSystemTransaction @SBCaller,@LogMsg,'SBError',0,'';

            END CONVERSATION @RecvReqDlgHandle;
        END
    END

    COMMIT TRANSACTION;

END
END

2 个答案:

答案 0 :(得分:1)

我注意到的一件事是,这件事似乎。大多数代码行似乎都在为服务代理对话框制作一条回复消息。

也就是说,服务代理的存在意味着您不必使用sp_xml_preparedocument来满足您的xml需求。看看XQuery。简而言之,这样的事情应该有效:

SELECT @ExecuteSQL = @RcvReqMsg.value('(RequestMsg/CommandParameters/ExecuteSQL)[1]', 'nvarchar(1000)')
   ,@CallBackSP = @RcvReqMsg.value('(RequestMsg/CommandParameters/CallBackSP)[1]', 'nvarchar(1000)')
   ,@SBCaller = @RcvReqMsg.value('(RequestMsg/CommandParameters/SBCaller)[1]', 'nvarchar(1000)')

其次,看起来消息包含要在包含此队列的数据库的上下文中执行的SQL。这些的表现形象是什么?那就是那些瓶颈吗?如果这些都很慢,那么添加服务代理就不会让事情变得更快

第三,您是否允许一次激活多个激活程序?检查sys.service_queues中的max_readers列以回答此问题。如果它设置为1并且您的流程不需要串行运行,请增加该数量以并行运行它们。

第四,看起来你已经编写了激活程序,在完成之前只处理一条消息。查看this tutorial中的示例。注意while (1=1)循环。这使得激活过程在完成当前消息

后返回到队列以获取另一条消息

最后,你为什么关心?服务代理是一种固有的异步技术。如果某人/某人正在等待处理给定的消息,我会质疑。

答案 1 :(得分:0)

首先,我建议将此威胁视为性能问题,并将其作为任何其他性能问题处理: measure 。有关如何测量会话或语句的等待,IO,CPU总体以及如何识别瓶颈的简要介绍和明确建议,请参阅How to analyse SQL Server performance。一旦你知道瓶颈在哪里,你就可以考虑解决它的方法。

现在针对SSB更具体的内容。我会说你的程序有三个对这个问题很有意思的组件:

  • 队列处理,即。 RECEIVEEND CONVERSATION
  • 消息解析(XML shredding)
  • 执行(EXECUTE(@ExecuteSQL)

对于队列处理,我建议Writing Service Broker Procedures了解如何加快速度。 RECEIVE TOP(1)是最慢的方法。批量处理更快,甚至更快,如果可能。要使批处理出列,您需要队列中的相关消息,这意味着SEND在单个会话句柄上显示许多消息,请参阅Reusing Conversation。这可能会显着地使应用程序复杂化。因此,我强烈建议您在做出如此剧烈的改变之前测量并确定瓶颈。

对于XML碎化我同意@BenThul,使用XML data type methods比使用MSXML程序更好。

最后有EXECUTE(@ExecuteSQL)。这对我们来说是一个黑盒子,只有你知道实际执行的是什么。不仅SQL执行的成本有多昂贵/复杂,而且还有可能阻塞的可能性。此后台执行与前端代码之间的锁争用可能会大大减慢队列处理速度。再次,衡量,你会知道。作为旁注:从您发布的数字来看,我希望问题出在这里。根据我的经验,一个完全按照你的方式执行的激活程序(RECEIVE TOP(1),XML解析,SEND响应),没有EXECUTE,应该以每秒约100条消息的速度进行排空你的队列2000个工作约20秒。你观察到一个慢得多的速度,这让我怀疑实际执行的SQL。

最后,轻松尝试:提升MAX_QUEUE_READERS(再次,正如@BenThul已经指出的那样):

 ALTER QUEUE HMSTargetQueueIntAct WITH ACTIVATION (MAX_QUEUE_READERS = 5)

这将允许并行处理请求。

您在程序中缺少正确的错误处理,您应该有一个BEGIN TRY/BEGIN CATCH块。请参阅Error Handling in Service Broker proceduresError Handling and ActivationHandling exceptions that occur during the RECEIVE statement in activated proceduresException handling and nested transactions