Service Broker - 会话生命周期?

Jon*_*ite 12 sql-server service-broker sql-server-2016

我们正在尝试让 Service Broker 在我们的环境中工作以解决业务案例。我不知道消息标题是否合适,但我的问题如下。但这可能不是一个好问题,所以在那之后是我们正在做的事情以及为什么我认为这是一个正确的问题。

在结束对话之前,应该在对话中发送多少条消息?

我们想使用 Service Broker 来异步更新结果表。结果表变平且快速。我们在基表上有触发器,它们发送带有表和主键的消息。我们有三个队列:

  • 低延迟 - 目标是处理 15 秒。它处理与特定项目相关的更改项目。
  • 批量队列 - 目标是处理 5 分钟。它处理影响数百(或数千)项的变化。它会列出受影响的项目并将它们提供给延迟低延迟队列。
  • 延迟低延迟 - 目标是处理 30 分钟。这会处理项目,但仅来自批量队列。

基本上,如果客户的信息更新,则会影响许多产品,因此会被发送到批量队列以进行较慢的处理。但是,如果产品更新,则会将其发送到低延迟队列。

我们重用类似于 Remus Rusanu 的博客http://rusanu.com/2007/04/25/reusing-conversations/ 的对话,除了我们根据主键的模数来做。这具有辅助主键重复数据删除的附带好处。

因此,我们正在重复使用对话并且符合我们的指导方针。使用两个线程,我能够每秒处理 125 条消息(人工丢弃数千条消息),这足以跟上生产速度(估计为 15 条消息/秒)。

但是,我们遇到的问题是,经过一段时间(约 4 小时或 120K 条消息)后,我们开始在 sysdesend 和队列表中看到阻塞和高争用。锁是 LCK_M_U 和 KEY 锁。有时,hobt 解析为 sysdesend,有时解析为特定的队列表 (queue_)。

我们有一个流程可以在 24 小时或 30 分钟不活动后结束对话,我们可以增加对话循环之前的时间。

我们使用的是 SQL 2016 Enterprise (13.0.4001.0)

  1. 触发火灾(发送到低延迟或批量)
  2. 查找或创建会话句柄。
  3. 发信息
  4. 队列激活程序
  5. 更新结果表

清理过程每 10 分钟运行一次,以查看是否有任何空闲对话。ltd 它连续发现它们超过 3 次,它将其标记为不活动并结束对话。

如果有任何可能有益的其他细节,请告诉我。我对 Service Broker 没有太多经验,所以我不知道我们的消息/秒是低、高还是无动于衷。

更新

所以我们今天再次尝试,遇到了同样的问题。我们将对话生命周期更改为 2 小时,但没有任何影响。所以我们然后实现了 150 技巧,它有同样的问题。

大量等待 SEND CONVERSATION,等待 sysdesend。有没有人有任何进一步的想法?

更新 2

我们今天进行了更长时间的测试,在 17 分钟的样本期间之一,我们在 4 个会话句柄上处理了 41K 条消息。我们能够跟上,除了最后当 sysdesend 和队列表上的锁变得太多时,我们在停止它之前开始落后。我们似乎没有问题处理消息,没有东西进入队列:我们可以将它们拉下来并以至少 5 倍的速度处理它们。我们的速度似乎因添加消息而受到限制。

在后来的测试中,我们删除了占消息 80% 的触发器之一。即使负载大大减少,我们也开始看到同样的等待。

更新 3

谢谢你,Remus 的建议(并感谢你发布了关于这个主题的如此出色的博客文章,它们有助于达到这一点)。

我们今天再次运行它并做得更好(因为在看到等待之前我们走了更长的时间,甚至在它使我们瘫痪之前更久)。所以,细节。

我们改变了:

  • 将每个线程的维护对话数从 1:1 增加到 2:1。基本上,我们有 4 个线程的 8 个会话句柄。

  • 合并批量队列(因为一条传入消息可能意味着数百条传出消息)以合并为更少、更大的消息。

关于这次尝试的注意事项:

  • 禁用目标队列激活程序。阻塞没有变化(我们等了 5 分钟)并且消息确实被发送到了 sys.transmission_queues。

  • 监控 sys.conversation_endpoints。这个数字很快从 0 上升到 13K,然后在一天中缓慢上升,在大约 5 小时后上升到大约 25K。阻塞直到达到 16K+/- 才开始发生

  • 我进入 DAC 并为队列运行 DBREINDEX 命令,尽管从查询中,在清理开始并将计数降至 0 之前,幽灵记录从未超过 200 左右。

  • 当我结束测试时,sysdesend 和 sysdercv 的计数相同,为 24,932。

  • 我们在 5 小时内处理了大约 31 万条消息。

我们在事情崩溃之前走得太远了,我真的以为这次我们会成功。明天我们将尝试强制消息通过线路。

Jon*_*ite 4

我知道回答你自己的问题是不好的形式,但我想为任何感兴趣的人结束这个问题。我们最终确实解决了这个问题,或者至少解决了足以满足我们的要求。我要感谢所有提供评论的人;Remus Rusanu 和 Kin 非常乐于助人。

我们的数据库非常繁忙并且处于 RCSI 模式。我们有多个(数千台)移动设备每 45 秒更新一次位置信息。通过这些更新,多个表的信息得到更新(设计不佳,因为我将易失性信息限制在单个表中,然后将其加入到结果中)。这些表与我们尝试异步生成报告信息的表相同,而不是让最终用户直接查看基表。

我们最初让触发器在每个更新/插入语句中的修改记录上执行游标操作(在大多数情况下应该是一行),并将消息中的每个主键发送到服务代理。在服务代理内部,尤其是批量队列是执行报告更新插入过程的进一步游标(每个主键执行一次)。

最终让我们工作的是什么:

  • 我们删除了光标并决定发送更大的消息。每个表每个用户事务仍然一条消息,但我们现在发送具有多个主键的消息。

  • 批量处理器还为每条消息发送多个键,这减少了在适当地将消息洗牌到其他队列时正在进行的发送对话的数量。

  • 最不稳定的表(我们的移动设备数据表)已删除其触发器。我们更新了 upsert 过程以包含适当的外键,现在我们只需在向用户获取结果时重新连接到该表即可。该表轻松贡献了我们一天必须处理的消息的 80%。

我们每天处理约 100 万条消息(没有移动表),并且绝大多数 (99%+) 消息都是在我们的目标内处理的。我们仍然偶尔会出现异常值,但考虑到这种情况的罕见性,它被认为是可以接受的。

影响因素:

  • 我在前面提到的对话清理过程中发现了一个错误,该错误实际上并没有适当地清理对话,而是过早地结束了对话。这导致我们的 sysdesend 计数永远不会超过几千(其中大部分来自使用 150 技巧)。

  • 触发器中的游标似乎比预期持有更多的锁定(即使使用 static、forward_only)。删除这些似乎使我们在 SEND CONVERSATION 上看到的锁定本质上更加短暂(或者至少我们看到的时间要少得多)。

  • 我们基本上并排运行两个解决方案(Service Broker 解决方案后端(用于在生产负载下进行测试))和当前解决方案(跨多个表的糟糕查询)。

作为一个附带好处,这发现了幽灵记录清理问题,虽然它不在 Service Broker 表(系统或队列)上,但它在我们的系统中相当猖獗,并且症状与我们的“没有明确原因”非常吻合我们有时会遇到的问题。对此的调查正在进行中,我们正在尝试找到对其有贡献的表,并且我们可能会定期重建它们的索引。

再一次感谢你。

*** 2021 年更新 ***

我对此再次投了赞成票,所以我想回来重新访问。在我提出这个问题后不久,我们最终放弃了 Service Broker,因为我们的事务工作负载仍然太高/太快,无法有效工作。我们最终为我们的高吞吐量消息系统迁移到这种类型的队列,并且效果很好。

http://kejser.org/implementing-message-queues-in-relational-databases/