原子独占SQL记录更新
我想更新表中的一条记录,以反映给定的客户端会话已经在多会话环境中获取了该记录(现在拥有该记录以便进一步更新)。到目前为止,我已经做到了:原子独占SQL记录更新,sql,sql-server,tsql,Sql,Sql Server,Tsql,我想更新表中的一条记录,以反映给定的客户端会话已经在多会话环境中获取了该记录(现在拥有该记录以便进一步更新)。到目前为止,我已经做到了: create procedure AcquireRow( @itemNo int, -- Item ID to acquire @sessNo int, -- Session ID @res char(1) out) -- Result as begin -- Attempt to acq
create procedure AcquireRow(
@itemNo int, -- Item ID to acquire
@sessNo int, -- Session ID
@res char(1) out) -- Result
as
begin
-- Attempt to acquire the row
update Items
set
State = 'A', -- 'A'=Acquired
SessionID = @sessNo
where ItemID = @itemNo
and State = 'N'; -- 'N'=Not acquired
-- Verify that the session actually acquired the row
set @res = 'T'; -- 'T'=Success
if @@rowcount = 0
set @res = 'F'; -- 'F'=Failure
end;
如果过程成功获取行,则out变量@state
设置为'T'
,否则设置为'F'
,表示失败
我的问题:这是否保证以原子方式工作,因此如果多个会话同时调用acquisitorow()
,则只有一个会话成功获取(更新)行?还是有更好的方法?我是否需要显式的行锁
修订:根据Remus的回答,我将重新排列代码,如下所示:
set @res = 'F';
update ...;
if @@rowcount > 0
set @res = 'T';
使用
输出
子句或将结果行的项目ID
赋值给更新
中的变量也是谨慎的。@@ROWCOUNT
非常容易出错。例如,在你的情况下。发件人:
简单的语句
分配始终设置@@ROWCOUNT
值为1。没有行被发送到服务器
客户这些陈述的例子
是:SET@local\u变量
要正确,您应该在更新后立即检查@ROWCOUNT。使用行锁更安全,但不是必需的。即使优化器决定使用页面锁,“acquire”语义也是正确的
我可能更喜欢另一种方法,即使用UPDATE
的OUTPUT
子句:
declare @updated table (ItemId int);
update Items
set ...
output inserted.ItemId
into @updated (ItemId)
where ...
该方案更能防止错误,也更灵活,因为它允许获取未知的ItemId:获取的id放在@updated表变量中,并可以返回给调用者
一般来说,为“acquire”使用real、committed和updates会遇到很多问题,因为您无法知道哪些行是真正获取的,哪些行是刚刚放弃的(客户端断开连接或崩溃,不发布“acquisition”).SQL server中的UPDATE语句在数据库引擎读取需要更新的行时获取更新锁,在写入时将转换为独占锁
当排他锁位于一行上时,所有其他事务都被阻止读取和写入它(除非读取事务处于读取未提交隔离状态,或者使用NOLOCK提示)
因此,是的,就目前而言,您的UPDATE语句是一个原子自动提交事务,因此对于同时调用它的多个会话来说,这应该是很好的。如果出于任何原因要将其分解为多个语句,则需要确保在SP中显式创建事务
Remus关于@ROWCOUNT和“acquire”的一般用法的评论是非常可靠的建议。有人问了一个非常类似的问题,但我找不到它……这个问题很相似,但不是我想的:谢谢,我不知道的特殊隐患。@@ROWCOUNT
。在我的原始代码的早期版本中,我在更新之前设置了@res
,因此它不会有那个问题。我还考虑过使用output
或set@updateID=ItemID=@itemNo
来捕获受影响行的ID,但对此不确定。谢谢你的信息。很棒的更新-你对此有任何MSDN或类似的参考资料吗?