我们正在尝试让 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)
- 触发触发(发送到低延迟或批量)
- 查找或创建对话句柄。
- 发信息
- 队列激活程序
- 更新结果表
清理过程每 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 万条消息。
在事情崩溃之前我们已经走了这么远,以至于我真的认为这次我们会成功。明天我们将尝试强制消息通过线路。
我知道回答你自己的问题是不好的形式,但我想为任何感兴趣的人关闭这个。我们终于设法解决了这个问题,或者至少解决了它足以满足我们的要求。我要感谢所有提供意见的人;Remus Rusanu 和 Kin,因为他们非常乐于助人。
我们的数据库非常繁忙并且处于 RCSI 模式。我们有多个(数千个)移动设备每 45 秒更新一次位置信息。通过这些更新,多个表的信息得到更新(糟糕的设计,因为我会将易失性信息限制在单个表中,然后将其加入以获得结果)。这些表与我们试图为其异步生成报告信息的表相同,而不是让最终用户直接访问基表。
我们最初让触发器在每个更新/插入语句(在大多数情况下应该是一行)中对修改的记录进行游标,并将消息中的每个主键发送到服务代理。在服务代理内部,尤其是批量队列是进一步的游标,它们执行报告的 upsert 过程(每个主键执行一次)。
是什么最终让我们工作:
我们删除了光标并决定发送更大的消息。每个表每个用户事务仍然有一条消息,但我们现在发送带有多个主键的消息。
批量处理器还为每条消息发送多个密钥,这减少了在适当时将消息混洗到另一个队列时正在进行的 SEND CONVERSATIONS 的数量。
最不稳定的表(我们的移动设备数据表)删除了它的触发器。我们更新了 upsert 过程以包含适当的外键,现在我们只需在向用户获取结果时加入该表。该表轻松贡献了我们一天必须处理的 80% 的消息。
我们每天处理约 100 万条消息(没有 Mobile 表),并且绝大多数 (99%+) 消息都在我们的目标内处理。我们仍然有偶尔的异常值,但考虑到它被认为是可以接受的罕见性质。
影响因素:
我在前面提到的对话清理过程中发现了一个错误,它实际上并没有适当地清理对话,而是过早地结束了它们。现在这导致我们的 sysdesend 计数永远不会超过几千(其中大部分来自使用 150 技巧)。
触发器中的游标似乎持有比预期更多的锁定(即使是静态的,forward_only)。删除这些似乎使我们在 SEND CONVERSATION 上看到的锁在本质上更加短暂(或者至少我们看到的时间要低得多)。
我们基本上并排运行了两个解决方案(Service Broker 解决方案后端(用于在生产负载下进行测试))和当前解决方案(跨越许多表的可怕查询)。
作为附带的好处,这发现了一个 Ghost Record Cleanup 问题,虽然它不在 Service Broker 表(系统或队列)上,但它在我们的系统中非常猖獗,并且症状与我们的“没有明确的原因”非常吻合我们有时会遇到的问题。对此的调查正在进行中,我们正试图找到对其有贡献的表,我们可能会定期重建它们的索引。
再一次感谢你。
*** 2021 年更新 ***
我对此再次投了赞成票,所以我想回来重新审视。在我提出这个问题后不久,我们最终放弃了 Service Broker,因为我们的事务工作量仍然太高/太快而无法有效工作。我们最终为我们的高吞吐量消息系统迁移到了这种队列样式,并且效果很好。
http://kejser.org/implementing-message-queues-in-relational-databases/