这个将需要一些解释。我所做的是在SQL Server 2005中创建一个特定的自定义消息队列。我有一个表包含消息,其中包含确认和完成的时间戳。调用程序执行以获取其队列中的下一条消息的存储过程也会确认该消息。到现在为止还挺好。那么,如果系统正在经历大量的事务(每分钟数千个),那么另一个执行存储过程的消息是否可能被确认,而另一个是否已经准备好了?让我通过在存储过程中显示我的SQL代码来帮助我们:
--Grab the next message id
declare @MessageId uniqueidentifier
set @MessageId = (select top(1) ActionMessageId from UnacknowledgedDemands);
--Acknowledge the message
update ActionMessages
set AcknowledgedTime = getdate()
where ActionMessageId = @MessageId
--Select the entire message
...
...
在上面的代码中,同时运行的另一个存储过程是否可以获得相同的id并尝试同时确认它?我(或我应该)可以实现某种锁定以防止另一个存储过程确认另一个存储过程正在查询的消息吗?
哇,这有甚么有意义吗?言辞有点难......答案 0 :(得分:7)
像这样的东西
--Grab the next message id
begin tran
declare @MessageId uniqueidentifier
select top 1 @MessageId = ActionMessageId from UnacknowledgedDemands with(holdlock, updlock);
--Acknowledge the message
update ActionMessages
set AcknowledgedTime = getdate()
where ActionMessageId = @MessageId
-- some error checking
commit tran
--Select the entire message
...
...
答案 1 :(得分:2)
这似乎是OUTPUT
可能有用的情况:
-- Acknowledge and grab the next message
declare @message table (
-- ...your `ActionMessages` columns here...
)
update ActionMessages
set AcknowledgedTime = getdate()
output INSERTED.* into @message
where ActionMessageId in (select top(1) ActionMessageId from UnacknowledgedDemands)
and AcknowledgedTime is null
-- Use the data in @message, which will have zero or one rows assuming
-- `ActionMessageId` uniquely identifies a row (strongly implied in your question)
...
...
在那里,我们更新并抓取同一操作中的行,告诉查询优化器完全我们正在做什么,允许它选择最精细的锁定并保持它为最简短的时间。 (虽然列前缀为INSERTED
,但OUTPUT
就像触发器一样,以UPDATE
表示,就像删除行并插入新行一样。)
我需要有关您的ActionMessages
和UnacknowledgedDemands
表(观看/ TVF /等等)的更多信息,更不用说对SQL Server的自动锁定有更多的了解,以说明{{1} }子句是必要的。它可以防止子选择和更新之间的竞争条件。我确定如果我们从and AcknowledgedTime is null
本身进行选择(例如ActionMessages
where AcknowledgedTime is null
top
而非update
,则我没有必要进行选择在UnacknowledgedDemands
)。我希望即使这是不必要的,也是无害的。
请注意,OUTPUT
位于SQL Server 2005及更高版本中。这就是你所说的你所使用的,但如果需要与老年SQL Server 2000安装的兼容性,你会想要采取另一种方式。
答案 2 :(得分:1)
@Kilhoffer:
在执行之前解析整个SQL批处理,因此SQL知道您将对表进行更新并从中进行选择。
编辑:此外,SQL不一定会锁定整个表 - 它可能只是锁定必要的行。有关SQL Server中锁定的概述,请参阅here。
答案 3 :(得分:1)
为什么不尝试这种方法,而不是显式锁定(通常由SQL Server升级到比预期更高的粒度),为什么不尝试这种方法:
declare @MessageId uniqueidentifier
select top 1 @MessageId = ActionMessageId from UnacknowledgedDemands
update ActionMessages
set AcknowledgedTime = getdate()
where ActionMessageId = @MessageId and AcknowledgedTime is null
if @@rowcount > 0
/* acknoweldge succeeded */
else
/* concurrent query acknowledged message before us,
go back and try another one */
锁定越少 - 你的并发性就越高。
答案 4 :(得分:0)
你真的要一个一个地处理事情吗?您是否应该让SQL Server确认所有未确认的消息与今天的日期并返回它们? (当然也都在交易中)
答案 5 :(得分:0)
答案 6 :(得分:-1)
您希望将代码包装在事务中,然后SQL Server将处理锁定适当的行或表。
begin transaction
--Grab the next message id
declare @MessageId uniqueidentifier
set @MessageId = (select top(1) ActionMessageId from UnacknowledgedDemands);
--Acknowledge the message
update ActionMessages
set AcknowledgedTime = getdate()
where ActionMessageId = @MessageId
commit transaction
--Select the entire message
...