假设“读取已提交快照”事务隔离设置为,以下语句是否“原子”,就意味着您将永远不会“丢失”并发增量?
update mytable set counter = counter + 1
我假设在一般情况下,如果此update语句是较大事务的一部分,则不是。例如,我认为这种情况是可能的:
在这种情况下,计数器最终不会仅增加1吗?如果这是事务中唯一的语句,会有所不同吗?
像stackoverflow这样的网站如何处理其问题视图计数器呢?还是“损失”某些增量的可能性被认为是可以接受的?
读取已提交的快照仅处理从表中选择数据的锁定。
但是,在t1和t2中,您正在更新数据,这是另一种情况。
当您更新计数器时,您将升级为写锁定(在行上),以防止发生其他更新。t2可以读取,但是t2将阻塞其UPDATE直到t1完成,并且t2将无法在t1之前提交(这与您的时间线相反)。只有事务之一可以更新计数器,因此在给出代码的情况下,两者都将正确更新计数器。(已测试)
读取已提交仅表示您只能读取已提交的值,但这并不意味着您具有可重复读取。因此,如果使用并依赖计数器变量,并打算在以后对其进行更新,则可能是在错误的隔离级别下运行事务。
您可以使用可重复的读取锁定,或者如果仅有时会更新计数器,则可以使用乐观锁定技术自己进行操作。例如带有计数器表的时间戳列或有条件的更新。
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更新它之前的计数器。
对于嵌套事务,如果t1在t1 COMMIT之后发出ROLLBACK,则counter返回其原始值,因为它还会撤消t2的提交。