3

我正在使用 SQL 服务代理。一个连接使用 XML 消息在队列上发送消息。我有一个独立的连接,即WAITFOR (RECEIVE TOP (1) ...), TIMEOUT 5000. 这会在WHILE (1=1)单个 Management Studio 查询或使用循环 reader.NextResult() 的单个 ADO.NET SQL 命令执行中循环。

所有这一切似乎都已正确配置,因为我最终得到了我期望的每一个结果。

但问题是最近的结果总是被阻止!

假设它一直在运行并且没有SENDs,所以它只是保持超时,每 5 秒在控制台上打印一次“None”。

那么现在我SEND从 SQL Management Studio 查询。该SEND命令立即成功完成。在正常超时间隔之前,我立即在控制台中获得“无” 。然后在下一个超时间隔,我的“更新”事件出现了!

如果我SEND在一系列“无”之后收到两条消息,那么我会立即收到“无”,然后是一条“更新”。同样,在下一个超时间隔,将出现第二个“更新”。

我可以SEND发送 10 条消息并立即收到一条“无”和九条“更新”。然后我等待五秒超时并获得最终的“更新”。

所有SENDs 都在同一个对话中,我从不结束对话。我不保留。

如果我在 RECEIVE 循环运行时对队列进行脏读,则队列始终为空。如果我停止接收循环,它会填满。如果我从另一个 Management Studio 查询窗口收到,则另一个线索是,当我停止查询时,始终会出现最终输出。这两个事实使我认为队列正在立即出队,但是阅读器方面正在锁定某些东西(但是什么??)。

起初,我认为这只是 Management Studio 的一种奇怪行为,我以前见过PRINT(?)有时会延迟。但我不希望在 ADO.NET 中出现同样的行为

也许它与 SSB 无关,只是 WAITFOR 或只是多个 ResultSet 的流式传输。我会调查这些选项,但我希望有人能在此期间认识到这一点。

这些症状你熟悉吗?

谢谢!

  • 是否有某种方法可以在下一个阻塞操作之前将结果刷新到连接?结果/结果集交付是否延迟?
  • 我没有在 sp_lock 结果之后立即看到任何额外的锁定SEND(或者其他情况)

对于@Rikalous,这里是 ADO.NET

using (var cmd = connection.CreateCommand())
{
    cmd.CommandTimeout = 0;
    cmd.CommandType = System.Data.CommandType.StoredProcedure;
    cmd.CommandText = "upWaitForReceive";

    using (var reader = cmd.ExecuteReader())
    {
        do
        {
            while (reader.Read()) // Individual messages received
            {
                string eventType = reader["EventType"] as string;

                // do something with the message
            }
        } while (reader.NextResult()); // next batch
    }
}

sql循环是:

SELECT 'Initialized' AS EventType

WHILE (1=1) BEGIN
    ;DECLARE 
        @ConversationHandle UNIQUEIDENTIFIER, 
        @MessageTypeName SYSNAME, 
        @MessageBody XML

    ;WAITFOR(
        RECEIVE TOP (1) @ConversationHandle = conversation_handle, @MessageTypeName = message_type_name, @MessageBody = message_body
        FROM [dbo].[{0}_EventsQueue]
    ), TIMEOUT 5000

    IF (@@ROWCOUNT = 0) BEGIN
        SELECT 'None' AS EventType; -- Allow the blocking call to spin anyway
        CONTINUE;
    END

    IF (@MessageTypeName = ...) BEGIN
        SELECT ...
    END ELSE IF (@MessageTypeName = 'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog') BEGIN
        END CONVERSATION @ConversationHandle
        SELECT @Message = 'Conversation Terminated' -- Not Expected
        RAISERROR (@Message, 11, 1)
        RETURN
    END
END

;END CONVERSATION @ConversationHandle -- This will never be reached.  But if it were, this is what should be done
4

2 回答 2

1

打印消息不会立即发送。我经常看到打印调试消息的任意高延迟。我认为 SQL Server 等待网络数据包已满,或其他某种缓冲。

如果您只是运行一个无限循环,每秒钟打印一条小消息,您可能会在 N 秒后一次性收到前 N 条消息。这可能仅适用于 TCP。也许共享内存更快,我不知道。

不过,我从未见过结果集会被延迟。因此,尝试通过执行SELECT 'msg'.

延迟写入客户端的问题似乎是众所周知的:

  1. http://connect.microsoft.com/SQLServer/feedback/details/124994/print-output-lags-in-stored-procedure-it-is-always-delay-during-execution-of-a-batch-or-存储过程
  2. https://stackoverflow.com/a/13047982/122718
  3. http://msdn.microsoft.com/en-us/library/ms178592.aspx

另一方面,代码中的所有副作用(写入表或其他 DML)无论如何都会立即发生。只有客户端应用程序接收PRINT延迟的消息。这种延迟根本不应该影响您的生产应用程序,因为所有有用的副作用都会立即发生。

简而言之,要么忽略问题,要么使用RAISERROR('Message', 0, 0) WITH NOWAIT. 仅当严重性大于 10 时才会抛出异常。

于 2013-09-16T13:28:03.323 回答
1

我相信 RAISERROR (...) WITH NOWAIT 应该刷新缓冲区,但我已经有一段时间没有使用它了。

于 2013-09-19T06:43:54.893 回答