【问题标题】:SQL Server, using a table as a queueSQL Server,使用表作为队列
【发布时间】:2011-01-14 00:02:21
【问题描述】:

我使用 SQL Server 2008 R2 作为排队机制。我将项目添加到表中,外部服务读取并处理这些项目。这很好用,但缺少一件事 - 我需要一种机制,我可以尝试从表中选择一行,如果没有,则阻塞直到存在(最好是特定时间段)。

谁能建议我如何实现这一目标?

【问题讨论】:

  • 这个问题在 SO 上被问了很多次;请搜索所以
  • 一般区域的良好链接。 rusanu.com/2010/03/26/using-tables-as-queues 编辑:在将项目添加到队列之前,不确定它是否能回答您关于阻塞的具体问题。另外,我不确定我之前是否在 SO 上看到过这个特定问题。
  • 您知道SQL Server 有实际 队列,对吧? msdn.microsoft.com/en-us/library/ms345108(v=sql.90).aspx
  • @Will 会看到“为什么不使用内置队列?”在上面的链接中。
  • @Mitch。也许使用 SQL Server 作为队列,但我没有问过我的具体问题——关于在检索操作期间等待数据。当然不经常。

标签: sql sql-server sql-server-2008 queue


【解决方案1】:

实现非池化阻塞出队的唯一方法是WAITFOR (RECEIVE)。这意味着 Service Broker 队列,以及所有额外的开销。

如果您使用普通的tables as queues,您将无法实现非轮询阻塞。您必须通过请求出队操作来轮询队列,如果它没有返回任何内容,请休眠并稍后重试。

恐怕我会不同意 Andomar 的观点:虽然他的回答是一个通用问题“表格中有行吗?”在排队时,由于重叠入队/出队的繁忙性质,检查这样的行是(几乎)保证在负载下出现死锁。在将表用作队列时,必须始终坚持基本的入队/出队操作,不要尝试花哨的东西。

【讨论】:

  • 我们使用常规 SQL 表作为队列,在高负载 (200K+/hr) 下,从未遇到过死锁。队列结构的本质支持这一点,您在一端(即表的末尾)插入并从另一端(即通过 SELECT TOP 1 的表的顶部)出列。在现实世界的使用中,事务性的事情会更复杂,但这就是它的要点
【解决方案2】:

“自从 SQL Server 2005 引入了 OUTPUT 子句,使用表作为队列不再是一个难题”。关于如何做到这一点的好帖子。

http://rusanu.com/2010/03/26/using-tables-as-queues/

【讨论】:

【解决方案3】:

我需要可以尝试的机制 从表中选择一行 并且,如果没有,则阻止直到 有(最好是针对特定的 一段时间)。

您可以每秒循环检查新行:

while not exists (select * from QueueTable)
    begin
    wait for delay '00:01'
    end

免责声明:这不是我将用于生产系统的代码,但它可以满足您的要求。

【讨论】:

  • 谢谢。这基本上是我目前正在使用的解决方法,但正如您所建议的那样,它并不是最好的解决方案。具体来说,我更喜欢不涉及循环的东西——某种等待操作,它要么超时,要么被添加到表中的新行(通过触发器?)启动。
【解决方案4】:

之前建议使用 Service Broker 的评论者可能给出了最佳答案。 Service Broker 允许您在等待更多输入时进行阻塞。

如果 Service Broker 太过分了,您应该考虑使用不同的方法来解决您的问题。你能提供更多关于你想要做什么的细节吗?

【讨论】:

  • 本质上,我正在寻找在分布式服务之间传递工作项的最佳方式(目前在同一台机器上,但将来它们可能在同一个 LAN 上的不同机器上)。在某些情况下,我需要将相同的工作项发送到两个不同的队列,这就是我最初选择尝试自己的实现的原因。任何建议都会受到欢迎。目前我正在考虑将服务代理与一组存储过程结合起来,这些过程将处理分发到多个队列。
  • 如果这些工作项的性质是异步的,您应该考虑类似 MSMQ。 MSMQ 可以跨服务/机器边界提供可靠的消息传递,包括传输重试等。但是,如果您想说“内部”SQL Server,Service Broker 正是您要寻找的。每个服务/机器都将托管 SQL Server Express(或更好)的副本,该副本将充当本地队列。然后它将管理将您的消息传输到远程服务的过程。它甚至可以管理您收到消息结束时的通知。它也支持多播。
【解决方案5】:

让我与您分享我在这方面的经验,您可能会发现它有帮助。

我的团队首先使用了为我们的异步服务(无论是 IIS 托管还是 WAS)提供服务的 MSMQ 事务队列。我们遇到的最大问题是重负载下的 MS DTC 问题,例如 100+ 条消息/秒负载;所需要的只是一个缓慢的数据库操作在某个地方开始导致超时异常,可以说 MS DTC 会使房子倒闭(如果事情变得足够糟糕,事务实际上会丢失),尽管我们不能 100% 确定根直到今天,我们确实怀疑集群环境中的 MS DTC 存在一些严重问题。

因此,我们开始寻找不同的解决方案。用于 Windows Server 的服务总线(Azure 服务总线的本地版本)看起来很有前景,但它是非事务性的,因此不符合我们的要求。

我们最终决定采用自己动手的方法,这是构建 Azure 服务总线的人向我们建议的方法,因为我们的事务要求。从本质上讲,我们遵循 Azure Worker Role 模型来创建一个工作角色,该角色将通过某个队列提供;轮询阻塞模型。

老实说,这对我们来说比我们使用过的其他任何东西都要好得多。这种服务的伪代码是:

hasMsg = true

while(true)

    if(!hasMsg)
         sleep

    msg = GetNextMessage

    if(msg == null)
        hasMsg = false
    else
        hasMsg = true

    Process(msg);

我们发现这种方式 CPU 使用率显着降低(低于传统 WCF 服务)。

当然,棘手的部分是处理交易。如果您希望从队列中读取服务的多个实例,则需要在 sql 中使用 read-past/updlock,并且还需要让您的 .net 服务以滚动方式加入事务 -服务失败时返回。在这种情况下,除了常规队列之外,您还需要使用重试/毒药队列作为表格。

【讨论】:

  • 这是你的应用层的伪代码吗?您如何实现 GetNextMessage 以便多个作品可以同时处理不同的消息?使用行锁?任何更多细节将不胜感激。
  • @David,我在示例中的最后一段解释了这是如何完成的。然而,它涉及更多一点,因为实际的队列操作需要特殊处理(即,如果事务应该回滚,我们仍然需要将消息移动到重试/中毒队列(如果适用))。 GetNextMessage 是微不足道的,这里的工作是让 SQL 做我们想做的事情。简而言之,使用了一个队列客户端类来处理在事务中的登记,并且在 SQL 端使用了一个 SQL CLR,它允许在 .NET 事务中登记。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-21
  • 1970-01-01
  • 2010-10-21
  • 2010-11-20
  • 2015-08-04
  • 2013-09-29
  • 1970-01-01
相关资源
最近更新 更多