在MS SQL Server中,有一种方法可以“原子地”增加用作计数器的列?

问题描述 投票:31回答:5

假设已读提交快照事务隔离设置,以下语句在您永远不会“丢失”并发增量的意义上是否“原子”?

update mytable set counter = counter + 1

我会假定在一般情况下,如果此update语句是较大事务的一部分,则不会。例如,我认为这种情况是可能的:

  • 更新事务1中的计数器
  • 做其他事情在交易#1
  • 更新计数器交易#2
  • 犯交易#2
  • 提交交易#1

在这种情况下,计数器最终不会仅增加1吗?如果这是事务中的唯一语句,是否会有所不同?

像stackoverflow这样的网站如何为其问题视图计数器处理此问题?还是“损失”一些增量的可能性只是被认为可以接受?

sql sql-server-2005 transactions
5个回答
13
投票

读取提交的快照仅处理从表中选择数据的锁定。

但是在t1和t2中,您正在更新数据,这是另一种情况。

当您更新计数器时,您将升级为写锁定(在行上),以防止发生其他更新。 t2可以读取,但是t2将阻塞其UPDATE,直到t1完成,并且t2将无法在t1之前提交(这与您的时间线相反)。只有事务之一可以更新计数器,因此在给出代码的情况下,两者都将正确更新计数器。 (测试)

  • 计数器= 0
  • t1更新计数器(计数器=> 1)
  • t2更新计数器(已阻止)
  • t1提交(计数器= 1)
  • t2解除阻止(现在可以更新计数器)(counter => 2)
  • t2提交

Read Committed只是意味着您只能读取已提交的值,但这并不意味着您具有可重复读取。因此,如果使用并依赖计数器变量,并打算在以后进行更新,则可能是在错误的隔离级别下运行事务。

您可以使用可重复的读取锁定,或者如果您只是有时会更新计数器,则可以使用乐观锁定技术自己进行操作。例如带计数器表的时间戳列,或有条件的更新。

DECLARE @CounterInitialValue INT
DECLARE @NewCounterValue INT
SELECT @CounterInitialValue = SELECT counter FROM MyTable WHERE MyID = 1234

-- do stuff with the counter value

UPDATE MyTable
   SET counter = counter + 1
WHERE
   MyID = 1234
   AND 
   counter = @CounterInitialValue -- prevents the update if counter changed.

-- the value of counter must not change in this scenario.
-- so we rollback if the update affected no rows
IF( @@ROWCOUNT = 0 )
    ROLLBACK

[此devx文章内容丰富,尽管谈论的功能仍处于测试阶段,因此可能并不完全准确。


更新:正如Justice所指出的,如果t2是t1中的嵌套事务,则语义是不同的。同样,两者都将正确更新计数器(+2),因为从t1内t2的角度来看,计数器已被更新一次。嵌套的t2无法访问t1更新它之前的计数器。

  • 计数器= 0
  • t1更新计数器(计数器=> 1)
  • t2更新计数器(嵌套事务)(计数器=> 2)
  • t2提交
  • t1提交(计数器= 2)

对于嵌套事务,如果t1在t1 COMMIT之后发出ROLLBACK,则计数器返回到其原始值,因为它也撤消了t2的提交。


27
投票

根据MSSQL帮助,您可以这样做:

UPDATE tablename SET counterfield = counterfield + 1 OUTPUT INSERTED.counterfield

这将字段更新一,并将更新后的值作为SQL记录集返回。


3
投票

不,不是。在共享模式下读取该值,然后在互斥模式下更新该值,因此可能发生多次读取。

要么使用Serializable级别,要么使用类似的东西>

update t
set counter = counter+1
from t with(updlock, <some other hints maybe>)
where foo = bar

1
投票

[心中只有一笔交易,最外面的一笔。内部事务更像是事务中的检查点。隔离级别仅影响同级最外事务,而不影响与父/子相关的事务。


0
投票

我使用此SP处理名称最初没有计数器的情况

© www.soinside.com 2019 - 2024. All rights reserved.