我们如何在SQL Server 2005中锁定表,我什至应该这样做?

时间:2020-03-05 18:51:50  来源:igfitidea点击:

这将需要一些解释。我要做的是在SQL Server 2005中创建特定的自定义消息队列。我有一个表,其中包含包含确认和完成时间戳记的消息。调用者为获取队列中的下一条消息而执行的存储过程也会确认该消息。到目前为止,一切都很好。好吧,如果系统正在经历大量事务(每分钟成千上万次),难道消息可能会在存储过程的另一次执行中被其他执行者自己确认吗?让我通过在存储的proc中显示我的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并尝试同时确认它吗?我是否可以(或者应该执行)某种锁定,以防止另一个存储的proc确认另一个正在存储的proc正在查询的消息?

哇,这有什么道理吗?说起来有点困难...

解决方案

回答

我们要将代码包装在事务中,然后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
...

回答

我们真的应该一对一地处理事情吗?我们是否不应该让SQL Server确认所有具有今天日期的未确认消息并返回它们? (当然也在交易中)

回答

@Kilhoffer:

整个SQL批处理都在执行之前进行了分析,因此SQL知道我们将要对该表进行更新并从中进行选择。

编辑:另外,SQL不一定会锁定整个表,它可能只是锁定必要的行。有关锁定SQL Server的概述,请参见此处。

回答

像这样

--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
...
...

回答

在此处和此处阅读有关SQL Server Select Locking的更多信息。 SQL Server能够在选择上调用表锁。在事务处理期间,表什么都不会发生。事务完成后,所有插入或者更新都将自行解决。

回答

代替显式锁定(通常由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 */

锁定的次数越少,我们拥有的并发性就越高。