将SQL Server用作具有多个客户端的数据库队列

Syn*_*sso 31 sql database sql-server concurrency

给定一个充当队列的表,如何最好地配置表/查询,以便多个客户端同时从队列中处理?

例如,下表指示了工作人员必须处理的命令.完成工作后,它会将处理后的值设置为true.

| ID | COMMAND | PROCESSED |
|  1 | ...     | true      |
|  2 | ...     | false     |
|  3 | ...     | false     |
Run Code Online (Sandbox Code Playgroud)

客户端可能会获得一个命令来执行此操作:

select top 1 COMMAND 
from EXAMPLE_TABLE 
with (UPDLOCK, ROWLOCK) 
where PROCESSED=false;
Run Code Online (Sandbox Code Playgroud)

但是,如果有多个worker,则每个都尝试获取ID = 2的行.只有第一个会得到悲观的锁定,其余的将等待.然后其中一个将获得第3行等.

什么查询/配置允许每个工作者客户端分别获得不同的行并同时处理它们?

编辑:

几个答案提出了使用表本身记录进程中状态的变化.我认为在单一交易中这是不可能的.(也就是说,如果在txn提交之前没有其他工作人员会看到它,那么更新状态的重点是什么?)或许建议是:

# start transaction
update to 'processing'
# end transaction
# start transaction
process the command
update to 'processed'
# end transaction
Run Code Online (Sandbox Code Playgroud)

这是人们通常接近这个问题的方式吗?在我看来,如果可能的话,DB会更好地处理问题.

Rem*_*anu 52

我建议你继续使用表格作为队列.正确实施的队列可以处理数千个并发用户,并且每分钟可以服务高达1/2百万的入队/出队操作.在SQL Server 2005之前,解决方案很麻烦,涉及混合a SELECTUPDATE单个事务,并提供恰当的锁定提示组合,如gbn链接的文章中所述.幸运的是,自SQL Server 2005出现OUTPUT子句以来,可以使用更优雅的解决方案,现在MSDN建议使用OUTPUT子句:

您可以在使用表作为队列的应用程序中使用OUTPUT,或者保存中间结果集.也就是说,应用程序不断地从表中添加或删除行

基本上,您需要完成3个难题才能使其以高度并发的方式工作:

1)你需要原子地出队.你必须找到行,skipp任何锁定的行,并在单个原子操作中将其标记为"出列",这就是该OUTPUT子句发挥作用的地方:

with CTE as (
  SELECT TOP(1) COMMAND, PROCESSED
  FROM TABLE WITH (READPAST)
  WHERE PROCESSED = 0)
UPDATE CTE
  SET PROCESSED = 1
  OUTPUT INSERTED.*;
Run Code Online (Sandbox Code Playgroud)

2)您必须使用PROCESSED列上最左侧的聚簇索引键构造表.如果ID使用了主键,则将其作为群集键中的第二列移动.关于是否在ID列上保留非聚簇键的争论是开放的,但我强烈支持在队列上没有任何辅助非聚簇索引:

CREATE CLUSTERED INDEX cdxTable on TABLE(PROCESSED, ID);
Run Code Online (Sandbox Code Playgroud)

3)您不能通过任何其他方式查询此表,而是通过Dequeue查询.尝试执行Peek操作或尝试将该表用作队列存储将很可能导致死锁并显着降低吞吐量.

基于处理位的原子出队,READPAST提示搜索元素到队列以及最左边的密钥在聚合索引上的组合确保了在高度并发负载下的非常高的吞吐量.


gbn*_*gbn 8

我的答案在这里向您展示如何使用表作为队列... SQL Server进程队列竞争条件

你基本上需要"ROWLOCK,READPAST,UPDLOCK"提示